def get_default_config_dir(filename=None): """ :param filename: if None, only the config path is returned, if provided, a path including the filename will be returned :type filename: string :returns: a file path to the config directory and optional filename :rtype: string """ if windows_check(): appDataPath = os.environ.get("APPDATA") if not appDataPath: import _winreg hkey = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders") appDataReg = _winreg.QueryValueEx(hkey, "AppData") appDataPath = appDataReg[0] _winreg.CloseKey(hkey) if filename: return os.path.join(appDataPath, "deluge", filename) else: return os.path.join(appDataPath, "deluge") else: from xdg.BaseDirectory import save_config_path try: if filename: return os.path.join(save_config_path("deluge"), filename) else: return save_config_path("deluge") except OSError, e: log.error("Unable to use default config directory, exiting... (%s)", e) sys.exit(1)
def __init__(self, icon_dir=None, no_icon=None): """ Initialises a new TrackerIcons object :param icon_dir: the (optional) directory of where to store the icons :type icon_dir: string :param no_icon: the (optional) path name of the icon to show when no icon can be fetched :type no_icon: string """ Component.__init__(self, "TrackerIcons") if not icon_dir: icon_dir = get_config_dir("icons") self.dir = icon_dir if not os.path.isdir(self.dir): os.makedirs(self.dir) self.icons = {} for icon in os.listdir(self.dir): if icon != no_icon: host = icon_name_to_host(icon) try: self.icons[host] = TrackerIcon(os.path.join(self.dir, icon)) except KeyError: log.warning("invalid icon %s", icon) if no_icon: self.icons[None] = TrackerIcon(no_icon) else: self.icons[None] = None self.icons[''] = self.icons[None] self.pending = {} self.redirects = {}
def _shutdown(self, *args, **kwargs): if os.path.exists(deluge.configmanager.get_config_dir("deluged.pid")): try: os.remove(deluge.configmanager.get_config_dir("deluged.pid")) except Exception, e: log.exception(e) log.error("Error removing deluged.pid!")
def delete_lockfile(): log.debug("Removing lockfile since it's stale.") try: os.remove(lockfile) os.remove(socket) except OSError, ex: log.error("Failed to delete lockfile: %s", ex)
def scrape_tracker(self): """Scrape the tracker""" try: self.handle.scrape_tracker() except Exception, e: log.debug("Unable to scrape tracker: %s", e) return False
def force_reannounce(self): """Force a tracker reannounce""" try: self.handle.force_reannounce() except Exception, e: log.debug("Unable to force reannounce: %s", e) return False
def start_daemon(self, port, config): """ Starts a daemon process. :param port: the port for the daemon to listen on :type port: int :param config: the path to the current config folder :type config: str :returns: True if started, False if not :rtype: bool :raises OSError: received from subprocess.call() """ try: if deluge.common.windows_check(): subprocess.Popen(["deluged", "--port=%s" % port, "--config=%s" % config]) else: subprocess.call(["deluged", "--port=%s" % port, "--config=%s" % config]) except OSError, e: from errno import ENOENT if e.errno == ENOENT: log.error(_("Deluge cannot find the 'deluged' executable, it is likely \ that you forgot to install the deluged package or it's not in your PATH.")) else: log.exception(e) raise e
def run_on_show_prefs(self): """This hook is run before the user is shown the preferences dialog. It is designed so that plugins can update their preference page with the config.""" log.debug("run_on_show_prefs") for function in self.hooks["on_show_prefs"]: function()
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!") elif self.forced_error and self.forced_error.was_paused: log.debug("Skip resuming Error state torrent that was originally paused.") 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.options["stop_at_ratio"]: if self.get_ratio() >= self.options["stop_ratio"]: #XXX: This should just be returned in the RPC Response, no event #self.signals.emit_event("torrent_resume_at_stop_ratio") return if self.options["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 if self.forced_error and not self.forced_error.restart_to_resume: self.clear_forced_error_state() elif self.state == "Error" and not self.forced_error: self.handle.clear_error()
def connect_peer(self, ip, port): """adds manual peer""" try: self.handle.connect_peer((ip, int(port)), 0) except Exception, e: log.debug("Unable to connect to peer: %s", e) return False
def on_alert_file_renamed(self, alert): log.debug("on_alert_file_renamed") log.debug("index: %s name: %s", alert.index, decode_string(alert.name)) try: torrent = self.torrents[str(alert.handle.info_hash())] torrent_id = str(alert.handle.info_hash()) except: return # We need to see if this file index is in a waiting_on_folder list folder_rename = False for i, wait_on_folder in enumerate(torrent.waiting_on_folder_rename): if alert.index in wait_on_folder[2]: folder_rename = True if len(wait_on_folder[2]) == 1: # This is the last alert we were waiting for, time to send signal component.get("EventManager").emit(TorrentFolderRenamedEvent(torrent_id, wait_on_folder[0], wait_on_folder[1])) # Empty folders are removed after libtorrent folder renames self.remove_empty_folders(torrent_id, wait_on_folder[0]) del torrent.waiting_on_folder_rename[i] self.save_resume_data((torrent_id,)) break # This isn't the last file to be renamed in this folder, so just # remove the index and continue torrent.waiting_on_folder_rename[i][2].remove(alert.index) if not folder_rename: # This is just a regular file rename so send the signal component.get("EventManager").emit(TorrentFileRenamedEvent(torrent_id, alert.index, alert.name)) self.save_resume_data((torrent_id,))
def add_torrent_url(self, url, options, headers=None): """ Adds a torrent from a url. Deluge will attempt to fetch the torrent from url prior to adding it to the session. :param url: the url pointing to the torrent file :type url: string :param options: the options to apply to the torrent on add :type options: dict :param headers: any optional headers to send :type headers: dict :returns: a Deferred which returns the torrent_id as a str or None """ log.info("Attempting to add url %s", url) def on_download_success(filename): # We got the file, so add it to the session f = open(filename, "rb") data = f.read() f.close() try: os.remove(filename) except Exception, e: log.warning("Couldn't remove temp file: %s", e) return self.add_torrent_file(filename, base64.encodestring(data), options)
def set_config(self, config): """Sets the config dictionary""" log.debug('seedtime %r' % config) log.debug('component state %r, component timer %r' % (self._component_state, self._component_timer)) for key in config.keys(): self.config[key] = config[key] self.config.save()
def __load_session_state(self): """Loads the libtorrent session state""" try: self.session.load_state(lt.bdecode( open(deluge.configmanager.get_config_dir("session.state"), "rb").read())) except Exception, e: log.warning("Failed to load lt state: %s", e)
def post_torrent_add(self, torrent_id): if not self.torrent_manager.session_started: return log.debug("seedtime post_torrent_add") if self.config["apply_stop_time"]: log.debug('applying stop.... time %r' % self.config['default_stop_time']) self.set_torrent(torrent_id, self.config["default_stop_time"])
def enable(self): log.info("applying prefs for automove") component.get("PluginManager").register_hook("on_apply_prefs", self.on_apply_prefs) component.get("PluginManager").register_hook("on_show_prefs", self.on_show_prefs) self.load_ui() self.dirty = False
def populate_list(self): if self.dirty : log.info("List in dirty state, don't reload prefs") return self.liststore.clear() for t in self.config["trackers"]: self.liststore.append(row=[ t["url"], t["dst"], t["cmd"] ])
def save_resume_data_file(self, resume_data=None): """ Saves the resume data file with the contents of self.resume_data. If `resume_data` is None, then we grab the resume_data from the file on disk, else, we update `resume_data` with self.resume_data and save that to disk. :param resume_data: the current resume_data, this will be loaded from disk if not provided :type resume_data: dict """ # Check to see if we're waiting on more resume data if self.num_resume_data or not self.resume_data: return path = os.path.join(get_config_dir(), "state", "torrents.fastresume") # First step is to load the existing file and update the dictionary if resume_data is None: resume_data = self.load_resume_data_file() resume_data.update(self.resume_data) self.resume_data = {} try: log.debug("Saving fastresume file: %s", path) fastresume_file = open(path, "wb") fastresume_file.write(lt.bencode(resume_data)) fastresume_file.flush() os.fsync(fastresume_file.fileno()) fastresume_file.close() except IOError: log.warning("Error trying to save fastresume file")
def save_resume_data_file(self, resume_data=None): """ Saves the resume data file with the contents of self.resume_data. If `resume_data` is None, then we grab the resume_data from the file on disk, else, we update `resume_data` with self.resume_data and save that to disk. :param resume_data: the current resume_data, this will be loaded from disk if not provided :type resume_data: dict """ # Check to see if we're waiting on more resume data if self.num_resume_data or not self.resume_data: return filepath = os.path.join(get_config_dir(), "state", "torrents.fastresume") filepath_tmp = filepath + ".tmp" filepath_bak = filepath + ".bak" # First step is to load the existing file and update the dictionary if resume_data is None: resume_data = self.load_resume_data_file() resume_data.update(self.resume_data) self.resume_data = {} try: os.remove(filepath_bak) except OSError: pass try: log.debug("Creating backup of fastresume at: %s", filepath_bak) os.rename(filepath, filepath_bak) except OSError, ex: log.error("Unable to backup %s to %s: %s", filepath, filepath_bak, ex)
def check_new_release(self): if self.new_release: log.debug("new_release: %s", self.new_release) if deluge.common.VersionSplit(self.new_release) > deluge.common.VersionSplit(deluge.common.get_version()): component.get("EventManager").emit(NewVersionAvailableEvent(self.new_release)) return self.new_release return False
def on_alert_metadata_received(self, alert): log.debug("on_alert_metadata_received") try: torrent = self.torrents[str(alert.handle.info_hash())] except: return torrent.on_metadata_received()
def run_on_apply_prefs(self): """This hook is run after the user clicks Apply or OK in the preferences dialog. """ log.debug("run_on_apply_prefs") for function in self.hooks["on_apply_prefs"]: function()
def remove(self, torrent_id, remove_data=False): """ Remove a torrent from the session. :param torrent_id: the torrent to remove :type torrent_id: string :param remove_data: if True, remove the downloaded data :type remove_data: bool :returns: True if removed successfully, False if not :rtype: bool :raises InvalidTorrentError: if the torrent_id is not in the session """ try: torrent_name = self.torrents[torrent_id].get_status(["name"])["name"] except KeyError: raise InvalidTorrentError("torrent_id not in session") # Emit the signal to the clients component.get("EventManager").emit(PreTorrentRemovedEvent(torrent_id)) try: self.session.remove_torrent(self.torrents[torrent_id].handle, 1 if remove_data else 0) except (RuntimeError, KeyError), e: log.warning("Error removing torrent: %s", e) return False
def on_drag_data_received_event(self, widget, drag_context, x, y, selection_data, info, timestamp): log.debug("Selection(s) dropped on main window %s", selection_data.data) if selection_data.get_uris(): process_args(selection_data.get_uris()) else: process_args(selection_data.data.split()) drag_context.finish(True, True)
def _on_menuitem_add_peer_activate(self, menuitem): """This is a callback for manually adding a peer""" log.debug("on_menuitem_add_peer") dialog_glade = gtk.glade.XML( pkg_resources.resource_filename("deluge.ui.gtkui", "glade/dgtkpopups.glade")) peer_dialog = dialog_glade.get_widget("connect_peer_dialog") txt_ip = dialog_glade.get_widget("txt_ip") response = peer_dialog.run() if response: value = txt_ip.get_text() if value and ':' in value: if ']' in value: #ipv6 ip = value.split("]")[0][1:] port = value.split("]")[1][1:] else: #ipv4 ip = value.split(":")[0] port = value.split(":")[1] if deluge.common.is_ip(ip): log.debug("adding peer %s to %s", value, self.torrent_id) client.core.connect_peer(self.torrent_id, ip, port) peer_dialog.destroy() return True
def enable(self): # Create the defaults with the core config core_config = component.get("Core").config DEFAULT_PREFS["low_down"] = core_config["max_download_speed"] DEFAULT_PREFS["low_up"] = core_config["max_upload_speed"] DEFAULT_PREFS["low_active"] = core_config["max_active_limit"] DEFAULT_PREFS["low_active_down"] = core_config["max_active_downloading"] DEFAULT_PREFS["low_active_up"] = core_config["max_active_seeding"] self.config = deluge.configmanager.ConfigManager("myscheduler.conf", DEFAULT_PREFS) self.torrent_states = deluge.configmanager.ConfigManager("myschedulerstates.conf", DEFAULT_STATES) self._cleanup_states() self.state = self.get_state() # Apply the scheduling rules self.do_schedule(False) # Schedule the next do_schedule() call for on the next hour now = time.localtime(time.time()) secs_to_next_hour = ((60 - now[4]) * 60) + (60 - now[5]) log.debug("Next schedule check in %s seconds" % secs_to_next_hour) self.timer = reactor.callLater(secs_to_next_hour, self.do_schedule) eventmanager = component.get("EventManager") eventmanager.register_event_handler("TorrentAddedEvent", self.update_torrent) eventmanager.register_event_handler("TorrentResumedEvent", self.update_torrent) eventmanager.register_event_handler("TorrentRemovedEvent", self._remove_torrent) eventmanager.register_event_handler("TorrentFinishedEvent", self._on_torrent_finished) # Register for config changes so state isn't overridden eventmanager.register_event_handler("ConfigValueChangedEvent", self.on_config_value_changed)
def _on_button_press_event(self, widget, event): """This is a callback for showing the right-click context menu.""" log.debug("on_button_press_event") # We only care about right-clicks if self.torrent_id and event.button == 3: self.peer_menu.popup(None, None, None, event.button, event.time) return True
def save_state(self): filename = "peers_tab.state" # Get the current sort order of the view column_id, sort_order = self.liststore.get_sort_column_id() # Setup state dict state = { "columns": {}, "sort_id": column_id, "sort_order": int(sort_order) if sort_order else None } for index, column in enumerate(self.listview.get_columns()): state["columns"][column.get_title()] = { "position": index, "width": column.get_width() } # Get the config location for saving the state file config_location = deluge.configmanager.get_config_dir() try: log.debug("Saving FilesTab state file: %s", filename) state_file = open(os.path.join(config_location, filename), "wb") cPickle.dump(state, state_file) state_file.close() except IOError, e: log.warning("Unable to save state file: %s", e)
def on_alert_file_error(self, alert): log.debug("on_alert_file_error: %s", decode_string(alert.message())) try: torrent = self.torrents[str(alert.handle.info_hash())] except: return torrent.update_state()
def get_selected_torrents(self): """Returns a list of selected torrents or None""" torrent_ids = [] try: paths = self.treeview.get_selection().get_selected_rows()[1] except AttributeError: # paths is likely None .. so lets return [] return [] try: for path in paths: try: row = self.treeview.get_model().get_iter(path) except Exception, e: log.debug("Unable to get iter from path: %s", e) continue child_row = self.treeview.get_model().convert_iter_to_child_iter(None, row) child_row = self.treeview.get_model().get_model().convert_iter_to_child_iter(child_row) if self.liststore.iter_is_valid(child_row): try: value = self.liststore.get_value(child_row, self.columns["torrent_id"].column_indices[0]) except Exception, e: log.debug("Unable to get value from row: %s", e) else: torrent_ids.append(value)
def start_looping(self): log.info('check interval loop starting') self.looping_call.start(self.config['interval'] * 3600.0)
def blacklistCommand(self, torrent_ids): log.info("blacklistCommand torrent running for {}".format(torrent_ids)) use_sonarr = self.config['enable_sonarr'] if self.config[ 'enable_sonarr'] else False use_radarr = self.config['enable_radarr'] if self.config[ 'enable_radarr'] else False use_lidarr = self.config['enable_lidarr'] if self.config[ 'enable_lidarr'] else False sonarr_list = self.sonarr.get_queue() if use_sonarr else {} radarr_list = self.radarr.get_queue() if use_radarr else {} lidarr_list = self.lidarr.get_queue() if use_lidarr else {} try: total_size = len(sonarr_list) + len(lidarr_list) + len(radarr_list) log.info("Size of lists: sonarr:{}, lidarr:{}, radarr:{}".format( len(sonarr_list), len(lidarr_list), len(radarr_list))) except Exception as e: log.error("Error summing lists: {}".format(e)) return if not total_size or total_size == 0: log.warning("No torrents found in queue") return label_str = None blackListedNum = 0 if not hasattr(torrent_ids, '__iter__'): torrent_ids = [torrent_ids] for i in torrent_ids: t = self.torrentmanager.torrents.get(i, None) log.debug("i = {}, t = {}, types = {}/{}".format( i, t, type(i), type(t))) if not t: log.warning("No torrent object for: {}".format(i)) continue else: name = t.get_status(['name'])['name'] if not name: log.warning( "Skipping blacklisting of torrent {}: could not get name" .format(i)) continue else: #try: label_str = component.get( "CorePlugin.Label")._status_get_label(i) if label_str and label_str in self.accepted_labels: if (label_str == 'tv-sonarr' and use_sonarr) or ( label_str == 'radarr' and use_radarr) or (label_str == 'lidarr' and use_lidarr): result = self.blacklistTorrent( i, t, label_str, name) if result: blackListedNum += 1 log.info("Blacklist request returned: {}".format( result)) else: log.info("Blacklisting not enabled for {}".format( label_str)) #except Exception as e: # log.warning("Error getting label for torrent {}: {}".format(name,e)) # continue self.torrent_states.save() return blackListedNum
def enable(self): log.debug("Enabling AutoRemovePlus...") self.builder = Gtk.Builder.new_from_file(get_resource("config.ui")) component.get("Preferences").add_page( "AutoRemovePlus", self.builder.get_object("prefs_box")) component.get("PluginManager").register_hook("on_apply_prefs", self.on_apply_prefs) component.get("PluginManager").register_hook("on_show_prefs", self.on_show_prefs) # Create and fill remove rule list self.rules = Gtk.ListStore(str, str) client.autoremoveplus.get_remove_rules().addCallback(self.cb_get_rules) # Fill list with logical functions self.sel_func_store = Gtk.ListStore(str) self.sel_func_store.append(["and"]) self.sel_func_store.append(["or"]) # Buttons to add/delete rules self._new_rule = self.builder.get_object("new_rule") self._new_rule.connect("clicked", self._do_new_rule) self._delete_rule = self.builder.get_object("delete_rule") self._delete_rule.connect("clicked", self._do_delete_rule) # Table to keep all rules self._blk_rules = self.builder.get_object("blk_rules") self._view = self._build_view_rules() window_rules = Gtk.ScrolledWindow() window_rules.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) window_rules.set_shadow_type(Gtk.ShadowType.IN) window_rules.add(self._view) self._blk_rules.add(window_rules) self._blk_rules.show_all() cell = Gtk.CellRendererText() cbo_remove = self.builder.get_object("cbo_remove") cbo_remove.pack_start(cell, True) cbo_remove.add_attribute(cell, 'text', 1) cbo_remove.set_model(self.rules) cbo_remove1 = self.builder.get_object("cbo_remove1") cbo_remove1.pack_start(cell, True) cbo_remove1.add_attribute(cell, 'text', 1) cbo_remove1.set_model(self.rules) cbo_sel_func = self.builder.get_object("cbo_sel_func") cbo_sel_func.set_model(self.sel_func_store) cbo_sel_func.set_active(0) self.builder.get_object("dummy").set_model(self.sel_func_store) self._new_tracker = self.builder.get_object("new_tracker") self._new_tracker.connect("clicked", self._do_new_tracker) self._delete_tracker = self.builder.get_object("delete_tracker") self._delete_tracker.connect("clicked", self._do_delete_tracker) self._blk_trackers = self.builder.get_object("blk_trackers") self._view_trackers = self._build_view_trackers() window = Gtk.ScrolledWindow() window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) window.set_shadow_type(Gtk.ShadowType.IN) window.add(self._view_trackers) self._blk_trackers.add(window) self._blk_trackers.show_all() self.builder.get_object("chk_remove").connect("toggled", self.on_click_remove) self.builder.get_object("chk_enabled").connect("toggled", self.on_click_enabled) self.builder.get_object("chk_rule_1").connect("toggled", self.on_click_chk_rule_1) self.builder.get_object("chk_rule_2").connect("toggled", self.on_click_chk_rule_2) def on_menu_show(menu, xxx_todo_changeme): (menu_item, toggled) = xxx_todo_changeme def set_ignored(ignored): # set_active will raise the 'toggled'/'activated' signals # so block it to not reset the value menu_item.handler_block(toggled) menu_item.set_active(False not in ignored) menu_item.handler_unblock(toggled) client.autoremoveplus.get_ignore([ t for t in component.get("TorrentView").get_selected_torrents() ]).addCallback(set_ignored) def on_menu_toggled(menu): client.autoremoveplus.set_ignore( component.get("TorrentView").get_selected_torrents(), menu.get_active()) self.menu = Gtk.CheckMenuItem(_("AutoRemovePlus Exempt")) self.menu.show() toggled = self.menu.connect('toggled', on_menu_toggled) torrentmenu = component.get("MenuBar").torrentmenu self.show_sig = torrentmenu.connect('show', on_menu_show, (self.menu, toggled)) self.realize_sig = torrentmenu.connect('realize', on_menu_show, (self.menu, toggled)) torrentmenu.append(self.menu) self.on_show_prefs()
def on_set_max_download_speed_per_torrent(self, key, value): log.debug("max_download_speed_per_torrent set to %s..", value) for key in self.torrents.keys(): self.torrents[key].set_max_download_speed(value)
def on_set_max_connections_per_torrent(self, key, value): """Sets the per-torrent connection limit""" log.debug("max_connections_per_torrent set to %s..", value) for key in self.torrents.keys(): self.torrents[key].set_max_connections(value)
def enable_plugin(self, plugin_name): """Enables a plugin""" if plugin_name not in self.available_plugins: log.warning("Cannot enable non-existant plugin %s", plugin_name) return if plugin_name in self.plugins: log.warning("Cannot enable already enabled plugin %s", plugin_name) return plugin_name = plugin_name.replace(" ", "-") egg = self.pkg_env[plugin_name][0] egg.activate() for name in egg.get_entry_map(self.entry_name): entry_point = egg.get_entry_info(self.entry_name, name) try: cls = entry_point.load() instance = cls(plugin_name.replace("-", "_")) except Exception, e: log.error("Unable to instantiate plugin!") log.exception(e) continue instance.enable() if self._component_state == "Started": component.start([instance.plugin._component_name]) plugin_name = plugin_name.replace("-", " ") self.plugins[plugin_name] = instance if plugin_name not in self.config["enabled_plugins"]: log.debug("Adding %s to enabled_plugins list in config", plugin_name) self.config["enabled_plugins"].append(plugin_name) log.info("Plugin %s enabled..", plugin_name)
def __init__(self): component.Component.__init__(self, "TorrentManager", interval=5, depend=["CorePluginManager"]) log.debug("TorrentManager init..") # Set the libtorrent session self.session = component.get("Core").session # Set the alertmanager self.alerts = component.get("AlertManager") # Get the core config self.config = ConfigManager("core.conf") # Make sure the state folder has been created if not os.path.exists(os.path.join(get_config_dir(), "state")): os.makedirs(os.path.join(get_config_dir(), "state")) # Create the torrents dict { torrent_id: Torrent } self.torrents = {} self.queued_torrents = set() # This is a list of torrent_id when we shutdown the torrentmanager. # We use this list to determine if all active torrents have been paused # and that their resume data has been written. self.shutdown_torrent_pause_list = [] # self.num_resume_data used to save resume_data in bulk self.num_resume_data = 0 # Keep track of torrents finished but moving storage self.waiting_on_finish_moving = [] # Keeps track of resume data that needs to be saved to disk self.resume_data = {} # Workaround to determine if TorrentAddedEvent is from state file self.session_started = False # Register set functions self.config.register_set_function("max_connections_per_torrent", self.on_set_max_connections_per_torrent) self.config.register_set_function("max_upload_slots_per_torrent", self.on_set_max_upload_slots_per_torrent) self.config.register_set_function("max_upload_speed_per_torrent", self.on_set_max_upload_speed_per_torrent) self.config.register_set_function("max_download_speed_per_torrent", self.on_set_max_download_speed_per_torrent) # Register alert functions self.alerts.register_handler("torrent_finished_alert", self.on_alert_torrent_finished) self.alerts.register_handler("torrent_paused_alert", self.on_alert_torrent_paused) self.alerts.register_handler("torrent_checked_alert", self.on_alert_torrent_checked) self.alerts.register_handler("tracker_reply_alert", self.on_alert_tracker_reply) self.alerts.register_handler("tracker_announce_alert", self.on_alert_tracker_announce) self.alerts.register_handler("tracker_warning_alert", self.on_alert_tracker_warning) self.alerts.register_handler("tracker_error_alert", self.on_alert_tracker_error) self.alerts.register_handler("storage_moved_alert", self.on_alert_storage_moved) self.alerts.register_handler("storage_moved_failed_alert", self.on_alert_storage_moved_failed) self.alerts.register_handler("torrent_resumed_alert", self.on_alert_torrent_resumed) self.alerts.register_handler("state_changed_alert", self.on_alert_state_changed) self.alerts.register_handler("save_resume_data_alert", self.on_alert_save_resume_data) self.alerts.register_handler("save_resume_data_failed_alert", self.on_alert_save_resume_data_failed) self.alerts.register_handler("file_renamed_alert", self.on_alert_file_renamed) self.alerts.register_handler("metadata_received_alert", self.on_alert_metadata_received) self.alerts.register_handler("file_error_alert", self.on_alert_file_error) self.alerts.register_handler("file_completed_alert", self.on_alert_file_completed)
class Config(object): """ This class is used to access/create/modify config files :param filename: the name of the config file :param defaults: dictionary of default values :param config_dir: the path to the config directory """ def __init__(self, filename, defaults=None, config_dir=None): self.__config = {} self.__set_functions = {} self.__change_callbacks = [] # These hold the version numbers and they will be set when loaded self.__version = {"format": 1, "file": 1} # This will get set with a reactor.callLater whenever a config option # is set. self._save_timer = None if defaults: for key, value in defaults.iteritems(): self.set_item(key, value) # Load the config from file in the config_dir if config_dir: self.__config_file = os.path.join(config_dir, filename) else: self.__config_file = deluge.common.get_default_config_dir(filename) self.load() def __contains__(self, item): return item in self.__config def __setitem__(self, key, value): """ See :meth:`set_item` """ return self.set_item(key, value) def set_item(self, key, value): """ Sets item 'key' to 'value' in the config dictionary, but does not allow changing the item's type unless it is None. If the types do not match, it will attempt to convert it to the set type before raising a ValueError. :param key: string, item to change to change :param value: the value to change item to, must be same type as what is currently in the config :raises ValueError: raised when the type of value is not the same as\ what is currently in the config and it could not convert the value **Usage** >>> config = Config("test.conf") >>> config["test"] = 5 >>> config["test"] 5 """ if isinstance(value, basestring): value = deluge.common.utf8_encoded(value) if not self.__config.has_key(key): self.__config[key] = value log.debug("Setting '%s' to %s of %s", key, value, type(value)) return if self.__config[key] == value: return # Do not allow the type to change unless it is None oldtype, newtype = type(self.__config[key]), type(value) if value is not None and oldtype != type(None) and oldtype != newtype: try: if oldtype == unicode: value = oldtype(value, "utf8") elif oldtype == bool: value = str_to_bool[value] else: value = oldtype(value) except (ValueError, KeyError): log.warning("Type '%s' invalid for '%s'", newtype, key) raise log.debug("Setting '%s' to %s of %s", key, value, type(value)) self.__config[key] = value # Run the set_function for this key if any from twisted.internet import reactor try: for func in self.__set_functions[key]: reactor.callLater(0, func, key, value) except KeyError: pass try: def do_change_callbacks(key, value): for func in self.__change_callbacks: func(key, value) reactor.callLater(0, do_change_callbacks, key, value) except: pass # We set the save_timer for 5 seconds if not already set if not self._save_timer or not self._save_timer.active(): self._save_timer = reactor.callLater(5, self.save) def __getitem__(self, key): """ See :meth:`get_item` """ return self.get_item(key) def get_item(self, key): """ Gets the value of item 'key' :param key: the item for which you want it's value :return: the value of item 'key' :raises KeyError: if 'key' is not in the config dictionary **Usage** >>> config = Config("test.conf", defaults={"test": 5}) >>> config["test"] 5 """ if isinstance(self.__config[key], str): try: return self.__config[key].decode("utf8") except UnicodeDecodeError: return self.__config[key] else: return self.__config[key] def register_change_callback(self, callback): """ Registers a callback function that will be called when a value is changed in the config dictionary :param callback: the function, callback(key, value) **Usage** >>> config = Config("test.conf", defaults={"test": 5}) >>> def cb(key, value): ... print key, value ... >>> config.register_change_callback(cb) """ self.__change_callbacks.append(callback) def register_set_function(self, key, function, apply_now=True): """ Register a function to be called when a config value changes :param key: the item to monitor for change :param function: the function to call when the value changes, f(key, value) :keyword apply_now: if True, the function will be called after it's registered **Usage** >>> config = Config("test.conf", defaults={"test": 5}) >>> def cb(key, value): ... print key, value ... >>> config.register_set_function("test", cb, apply_now=True) test 5 """ log.debug("Registering function for %s key..", key) if key not in self.__set_functions: self.__set_functions[key] = [] self.__set_functions[key].append(function) # Run the function now if apply_now is set if apply_now: function(key, self.__config[key]) return def apply_all(self): """ Calls all set functions **Usage** >>> config = Config("test.conf", defaults={"test": 5}) >>> def cb(key, value): ... print key, value ... >>> config.register_set_function("test", cb, apply_now=False) >>> config.apply_all() test 5 """ log.debug("Calling all set functions..") for key, value in self.__set_functions.iteritems(): for func in value: func(key, self.__config[key]) def apply_set_functions(self, key): """ Calls set functions for `:param:key`. :param key: str, the config key """ log.debug("Calling set functions for key %s..", key) if key in self.__set_functions: for func in self.__set_functions[key]: func(key, self.__config[key]) def load(self, filename=None): """ Load a config file :param filename: if None, uses filename set in object initialization """ if not filename: filename = self.__config_file try: data = open(filename, "rb").read() except IOError, e: log.warning("Unable to open config file %s: %s", filename, e) return objects = find_json_objects(data) if not len(objects): # No json objects found, try depickling it try: self.__config.update(pickle.loads(data)) except Exception, e: log.exception(e) log.warning("Unable to load config file: %s", filename)
# Initialize gettext try: locale.setlocale(locale.LC_ALL, '') if hasattr(locale, "bindtextdomain"): locale.bindtextdomain( "deluge", pkg_resources.resource_filename("deluge", "i18n")) if hasattr(locale, "textdomain"): locale.textdomain("deluge") gettext.bindtextdomain("deluge", pkg_resources.resource_filename("deluge", "i18n")) gettext.textdomain("deluge") gettext.install("deluge", pkg_resources.resource_filename("deluge", "i18n")) except Exception, e: log.error("Unable to initialize gettext/locale!") log.exception(e) import __builtin__ __builtin__.__dict__["_"] = lambda x: x import deluge.component as component from deluge.ui.client import client from mainwindow import MainWindow from menubar import MenuBar from toolbar import ToolBar from torrentview import TorrentView from torrentdetails import TorrentDetails from sidebar import SideBar from filtertreeview import FilterTreeView from preferences import Preferences from systemtray import SystemTray
try: from deluge.log import LOG as log except Exception as e: print 'Telegramer: Exception - %s' % str(e) try: import gtk import deluge.common from common import get_resource, REGEX_TMPL_FILE_NAME from deluge.ui.client import client import deluge.component as component from deluge.plugins.pluginbase import GtkPluginBase except ImportError as e: log.error('Telegramer: Import error - %s', str(e)) class GtkUI(GtkPluginBase): def enable(self): self.glade = gtk.glade.XML(get_resource("config.glade")) self.glade.signal_autoconnect({ "on_button_test_clicked": self.on_button_test_clicked, "on_button_save_clicked": self.on_button_save_clicked, "on_button_reload_clicked": self.on_button_reload_clicked }) component.get("Preferences").add_page("Telegramer", self.glade.get_widget("prefs_box")) component.get("PluginManager").register_hook("on_apply_prefs", self.on_apply_prefs) component.get("PluginManager").register_hook("on_show_prefs", self.on_show_prefs) def disable(self):
def on_set_max_upload_slots_per_torrent(self, key, value): """Sets the per-torrent upload slot limit""" log.debug("max_upload_slots_per_torrent set to %s..", value) for key in self.torrents.keys(): self.torrents[key].set_max_upload_slots(value)
def periodicScan(self, *args, **kwargs): log.info( "AutoRemovePlus: Running check. Interval is {} minutes".format( round(self.config['interval'] * 60.0, 1))) try: max_seeds = int(self.config['max_seeds']) count_exempt = self.config['count_exempt'] remove_data = self.config['remove_data'] seed_remove_data = self.config['seed_remove_data'] exemp_trackers = self.config['trackers'] exemp_labels = self.config['labels'] min_val = float(self.config['min']) max_val2 = float(self.config['min2']) remove = self.config['remove'] enabled = self.config['enabled'] tracker_rules = self.config['tracker_rules'] rule_1_chk = self.config['rule_1_enabled'] rule_2_chk = self.config['rule_2_enabled'] seedtime_limit = float(self.config['seedtime_limit']) seedtime_pause = float(self.config['seedtime_pause']) pause_torrents = self.config['pause_torrents'] labels_enabled = False use_sonarr = self.config['enable_sonarr'] if self.config[ 'enable_sonarr'] else False use_radarr = self.config['enable_radarr'] if self.config[ 'enable_radarr'] else False use_lidarr = self.config['enable_lidarr'] if self.config[ 'enable_lidarr'] else False sonarr_list = self.sonarr.get_queue() if use_sonarr else {} radarr_list = self.radarr.get_queue() if use_radarr else {} lidarr_list = self.lidarr.get_queue() if use_lidarr else {} #prevent hit & run seedtime_pause = seedtime_pause if seedtime_pause > 20.0 else 20.0 seedtime_limit = seedtime_limit if seedtime_limit > 24.0 else 24.0 log.debug("Using sonarr: {}, radarr: {}, lidarr: {}".format( use_sonarr, use_radarr, use_lidarr)) log.info("Size of lists: sonarr:{}, lidarr:{}, radarr:{}".format( len(sonarr_list), len(lidarr_list), len(radarr_list))) #response = self.sonarr.delete_queueitem('1771649588') #log.info("Delete response:{}".format(response)) except Exception as e: log.error("Error reading config: {}".format(e)) return False if 'Label' in component.get("CorePluginManager").get_enabled_plugins(): labels_enabled = True label_rules = self.config['label_rules'] else: log.warning("WARNING! Label plugin not active") log.debug("No labels will be checked for exemptions!") label_rules = [] # Negative max means unlimited seeds are allowed, so don't do anything if max_seeds < 0: return torrent_ids = self.torrentmanager.get_torrent_list() log.info("Number of torrents: {0}".format(len(torrent_ids))) # If there are less torrents present than we allow # then there can be nothing to do if len(torrent_ids) <= max_seeds: return torrents = [] ignored_torrents = [] # relevant torrents to us exist and are finished for i in torrent_ids: t = self.torrentmanager.torrents.get(i, None) try: ignored = self.torrent_states[i] except KeyError as e: ignored = False ex_torrent = False trackers = t.trackers # check if trackers in exempted tracker list for tracker, ex_tracker in ((t, ex_t) for t in trackers for ex_t in exemp_trackers): if (tracker['url'].find(ex_tracker.lower()) != -1): log.debug("Found exempted tracker: %s" % (ex_tracker)) ex_torrent = True # check if labels in exempted label list if Label plugin is enabled if labels_enabled: try: # get label string label_str = component.get( "CorePlugin.Label")._status_get_label(i) # if torrent has labels check them labels = [label_str] if len(label_str) > 0 else [] for label, ex_label in ((l, ex_l) for l in labels for ex_l in exemp_labels): if (label.find(ex_label.lower()) != -1): log.debug("Found exempted label: %s" % (ex_label)) ex_torrent = True except Exception as e: log.warning("Cannot obtain torrent label: {}".format(e)) # if torrent tracker or label in exemption list, or torrent ignored # insert in the ignored torrents list (ignored_torrents if ignored or ex_torrent else torrents)\ .append((i, t)) log.info("Number of ignored torrents: {0}".format( len(ignored_torrents))) # now that we have trimmed active torrents # check again to make sure we still need to proceed if len(torrents) +\ (len(ignored_torrents) if count_exempt else 0) <= max_seeds: return # if we are counting ignored torrents towards our maximum # then these have to come off the top of our allowance if count_exempt: max_seeds -= len(ignored_torrents) if max_seeds < 0: max_seeds = 0 # Alternate sort by primary and secondary criteria torrents.sort( key=lambda x: (filter_funcs.get(self.config['filter'], _get_ratio) (x), filter_funcs.get(self.config['filter2'], _get_ratio)(x)), reverse=False) changed = False # remove or pause these torrents for i, t in reversed(torrents[max_seeds:]): name = t.get_status(['name'])['name'] log.debug("Now processing name = {}, type = {}".format( name, type(name))) # check if free disk space below minimum if self.check_min_space(): break # break the loop, we have enough space if enabled: # Get result of first condition test filter_1 = filter_funcs.get(self.config['filter'], _get_ratio)( (i, t)) <= min_val # Get result of second condition test #chosen_func = self.config['filter2'] # prevent hit and runs max_val2 = max_val2 if max_val2 > 0.5 else 0.5 #log.info("Chosen filter2 : {}, cut-off: {}".format(chosen_func,max_val2)) filter_2 = filter_funcs.get(self.config['filter2'], _get_ratio)((i, t)) >= max_val2 specific_rules = self.get_torrent_rules( i, t, tracker_rules, label_rules) # Sort rules according to logical operators, AND is evaluated first specific_rules.sort(key=lambda rule: rule[0]) remove_cond = False seed_remove_cond = False #for removing finished torrents # If there are specific rules, ignore general remove rules if specific_rules: remove_cond = filter_funcs.get(specific_rules[0][1])((i,t)) \ >= specific_rules[0][2] for rule in specific_rules[1:]: check_filter = filter_funcs.get(rule[1])((i,t)) \ >= rule[2] remove_cond = sel_funcs.get(rule[0])( (check_filter, remove_cond)) seed_remove_cond = remove_cond elif rule_1_chk and rule_2_chk: # If both rules active use custom logical function remove_cond = sel_funcs.get(self.config['sel_func'])( (filter_1, filter_2)) elif rule_1_chk and not rule_2_chk: # Evaluate only first rule, since the other is not active remove_cond = filter_1 elif not rule_1_chk and rule_2_chk: # Evaluate only second rule, since the other is not active remove_cond = filter_2 # If logical functions are satisfied remove or pause torrent # add check that torrent is not completed try: name = t.get_status(['name'])['name'] age = _age_in_days((i, t)) # age in days seedtime = round( t.get_status(['seeding_time'])['seeding_time'] / 3600, 2) #seed time in hours ratio = t.get_status(['ratio'])['ratio'] availability = t.get_status(['distributed_copies' ])['distributed_copies'] time_last_transfer = _time_last_transfer( (i, t)) # in hours time_seen_complete = _time_seen_complete( (i, t)) #seen complete in hours isFinished = t.get_status(['is_finished'])['is_finished'] paused = t.get_status(['paused'])['paused'] hash = t.get_status(['hash'])['hash'].upper() except Exception as e: log.error("Error with torrent: {}".format(e)) continue if time_seen_complete: log.debug( "Processing torrent: {}, last transfer: {} h, last seen complete: {} h, paused: {}" .format(name, time_last_transfer, time_seen_complete, paused)) if not isFinished: try: label_str = component.get( "CorePlugin.Label")._status_get_label(i) if not label_str: log.warning("Torrent: {}, label = {}".format( name, label_str)) except Exception as e: log.error( "Error getting label for torrent {}: {}".format( name, e)) label_str = 'none' log.debug( "Processing unfinished torrent {}, label = {}".format( name, label_str)) if remove_cond: #pause torrents if selected if pause_torrents: if not paused: log.info( "AutoRemovePlus: Pausing torrent {} due to availability = {}, age = {}, time_last_transfer = {}" .format(name, availability, age, time_last_transfer)) self.pause_torrent(t) #user has selected to remove torrents if remove: # blacklist if label_str and label_str in self.accepted_labels: if (label_str == 'tv-sonarr' and use_sonarr ) or (label_str == 'radarr' and use_radarr) or (label_str == 'lidarr' and use_lidarr): result = self.blacklistTorrent( i, t, label_str, name) log.info( "Blacklist request for {} returned: {}" .format(name, result)) else: log.warning( "No matching label {} for torrent {}". format(label_str, name)) # remove using local method result = self.remove_torrent(i, remove_data) log.info( "AutoRemovePlus: removing unfinished torrent {} with data = {} using internal method: {}" .format(name, remove_data, result)) else: # is finished log.debug( "Fin.: {}, seed time:{}/{}, ratio: {}, spec. rules = {}, sr cond. = {}/{},isfinished = {}, hash = {}" .format(name, seedtime, seedtime_limit, ratio, specific_rules, remove_cond, seed_remove_cond, isFinished, hash)) if (not specific_rules) or (seed_remove_cond): #remove condition if seedtime > seedtime_limit: #seed_remove_data decides if user wants data removed or not self.remove_torrent(i, seed_remove_data) changed = True log.info( "AutoRemovePlus: removing torrent from seed: {} due to seed time = {}/{} h" .format(name, seedtime, seedtime_limit)) #pause condition elif seedtime > seedtime_pause: if pause_torrents: try: #paused = t.get_status(['paused'])['paused'] if not paused: self.pause_torrent(t) #changed = True log.info( "AutoRemovePlus: pausing finished torrent {} with seedtime = {}/{} h, ratio = {}, rules = {}, sr-cond = {}/{}" .format(name, seedtime, seedtime_pause, ratio, specific_rules, remove_cond, seed_remove_cond)) else: log.debug( "AutoRemovePlus: torrent is already paused: {}" .format(name)) except Exception as e: log.warning( "AutoRemovePlus: error with pausing torrent: {}" .format(name)) # If a torrent exemption state has been removed save changes if changed: self.torrent_states.save()
def on_statusbar_click(self, widget, event): log.debug("on_statusbar_click") self.run()
def _get_ratio(i_t): (i, t) = i_t log.debug("Get ratio: i = {}, t = {}".format(i, t)) return t.get_ratio()
def win_handler(ctrl_type): log.debug("ctrl_type: %s", ctrl_type) if ctrl_type in (CTRL_CLOSE_EVENT, CTRL_SHUTDOWN_EVENT): reactor.stop() return 1
def pause_torrent(self, torrent): try: torrent.pause() except Exception as e: log.warning( "AutoRemovePlus: Problems pausing torrent: {}".format(e))
def enable(self): self.config = deluge.configmanager.ConfigManager( "notifications-web.conf", DEFAULT_PREFS ) log.debug("Enabling Web UI notifications")
def cb_get_config(self, config): """callback for on show_prefs""" log.debug('cb get config seedtime') self.glade.get_widget("chk_apply_stop_time").set_active(config["apply_stop_time"]) self.glade.get_widget("chk_remove_torrent").set_active(config["remove_torrent"]) self.glade.get_widget("txt_default_stop_time").set_text('%.02f' % config["default_stop_time"])
objects = find_json_objects(data) if not len(objects): # No json objects found, try depickling it try: self.__config.update(pickle.loads(data)) except Exception, e: log.exception(e) log.warning("Unable to load config file: %s", filename) elif len(objects) == 1: start, end = objects[0] try: self.__config.update(json.loads(data[start:end])) except Exception, e: log.exception(e) log.warning("Unable to load config file: %s", filename) elif len(objects) == 2: try: start, end = objects[0] self.__version.update(json.loads(data[start:end])) start, end = objects[1] self.__config.update(json.loads(data[start:end])) except Exception, e: log.exception(e) log.warning("Unable to load config file: %s", filename) log.debug("Config %s version: %s.%s loaded: %s", filename, self.__version["format"], self.__version["file"], self.__config)
def stop_reactor(result): try: reactor.stop() except ReactorNotRunning: log.debug("Attempted to stop the reactor but it is not running...")
def set_item(self, key, value): """ Sets item 'key' to 'value' in the config dictionary, but does not allow changing the item's type unless it is None. If the types do not match, it will attempt to convert it to the set type before raising a ValueError. :param key: string, item to change to change :param value: the value to change item to, must be same type as what is currently in the config :raises ValueError: raised when the type of value is not the same as\ what is currently in the config and it could not convert the value **Usage** >>> config = Config("test.conf") >>> config["test"] = 5 >>> config["test"] 5 """ if isinstance(value, basestring): value = deluge.common.utf8_encoded(value) if not self.__config.has_key(key): self.__config[key] = value log.debug("Setting '%s' to %s of %s", key, value, type(value)) return if self.__config[key] == value: return # Do not allow the type to change unless it is None oldtype, newtype = type(self.__config[key]), type(value) if value is not None and oldtype != type(None) and oldtype != newtype: try: if oldtype == unicode: value = oldtype(value, "utf8") elif oldtype == bool: value = str_to_bool[value] else: value = oldtype(value) except (ValueError, KeyError): log.warning("Type '%s' invalid for '%s'", newtype, key) raise log.debug("Setting '%s' to %s of %s", key, value, type(value)) self.__config[key] = value # Run the set_function for this key if any from twisted.internet import reactor try: for func in self.__set_functions[key]: reactor.callLater(0, func, key, value) except KeyError: pass try: def do_change_callbacks(key, value): for func in self.__change_callbacks: func(key, value) reactor.callLater(0, do_change_callbacks, key, value) except: pass # We set the save_timer for 5 seconds if not already set if not self._save_timer or not self._save_timer.active(): self._save_timer = reactor.callLater(5, self.save)
def blacklistTorrent(self, i, t, label_str, name): hash = t.get_status(['hash'])['hash'].upper() if label_str and label_str in self.accepted_labels: mediaObject = self.sonarr if label_str == 'tv-sonarr' else self.radarr if label_str == 'radarr' else self.lidarr elif not label_str: log.warning("No label for {}".format(name)) return else: log.warning("Unknown label for torrrent {}".format(name)) return if mediaObject: mediaList = mediaObject.get_queue() log.debug("Size of media list: {}".format(len(mediaList))) if hash in mediaList: id = str(mediaList[hash].get('id')) log.info("hash: {}, id: {},type = {}".format( hash, id, type(id))) #blacklist from PVR response = mediaObject.delete_queueitem(id) changed = True log.info("Blacklist request for torrent {} returned {}".format( name, response)) isFinished = t.get_status(['is_finished'])['is_finished'] remove_data = self.config[ 'seed_remove_data'] if isFinished else self.config[ 'remove_data'] #remove from deluge result = self.remove_torrent(i, remove_data) log.info("Removing {} torrent {} {} data returned: {}".format( 'unfinished' if not isFinished else 'finished', name, 'with' if remove_data else 'without', result)) return result else: log.warning( "Could not blacklist torrent {}: not in server queue: {}". format(name, hash)) log.debug("List: {}".format(mediaList)) isFinished = t.get_status(['is_finished'])['is_finished'] remove_data = self.config[ 'seed_remove_data'] if isFinished else self.config[ 'remove_data'] #remove from deluge result = self.remove_torrent(i, remove_data) log.info("Removing {} torrent {} {} data. Result: {}".format( 'unfinished' if not isFinished else 'finished', name, 'with' if remove_data else 'without', result)) return result else: log.warning( "Upstream server not found for label: {}".format(label_str)) return
def on_select_time(self, widget=None, time=None): log.debug("select seed stop time:%s,%s" % (time ,self.get_torrent_ids()) ) for torrent_id in self.get_torrent_ids(): client.seedtime.set_torrent(torrent_id, time)
def on_apply_prefs(self): log.debug("applying prefs for Ratio") config = { 'persistent': self.glade.get_widget("persistent").get_active(), } client.ratio.set_config(config)
def log_failure(failure, action): log.error("Encountered error attempting to %s: %s" % \ (action, failure.getErrorMessage()))
def on_reset_ratio_button_clicked(self, widget): log.debug('on_reset_ratio_button_clicked') client.ratio.reset_ratio()
def __update_buttons(self): """ Updates the buttons states. """ if len(self.liststore) == 0: # There is nothing in the list self.glade.get_widget("button_startdaemon").set_sensitive(True) self.glade.get_widget("button_connect").set_sensitive(False) self.glade.get_widget("button_removehost").set_sensitive(False) self.glade.get_widget("image_startdaemon").set_from_stock( gtk.STOCK_EXECUTE, gtk.ICON_SIZE_MENU) self.glade.get_widget("label_startdaemon").set_text("_Start Daemon") model, row = self.hostlist.get_selection().get_selected() if not row: return # Get some values about the selected host status = model[row][HOSTLIST_COL_STATUS] host = model[row][HOSTLIST_COL_HOST] log.debug("Status: %s", status) # Check to see if we have a localhost entry selected localhost = False if host in ("127.0.0.1", "localhost"): localhost = True # Make sure buttons are sensitive at start self.glade.get_widget("button_startdaemon").set_sensitive(True) self.glade.get_widget("button_connect").set_sensitive(True) self.glade.get_widget("button_removehost").set_sensitive(True) # See if this is the currently connected host if status == "Connected": # Display a disconnect button if we're connected to this host self.glade.get_widget("button_connect").set_label("gtk-disconnect") self.glade.get_widget("button_removehost").set_sensitive(False) else: self.glade.get_widget("button_connect").set_label("gtk-connect") if status == "Offline" and not localhost: self.glade.get_widget("button_connect").set_sensitive(False) # Check to see if the host is online if status == "Connected" or status == "Online": self.glade.get_widget("image_startdaemon").set_from_stock( gtk.STOCK_STOP, gtk.ICON_SIZE_MENU) self.glade.get_widget("label_startdaemon").set_text( _("_Stop Daemon")) # Update the start daemon button if the selected host is localhost if localhost and status == "Offline": # The localhost is not online self.glade.get_widget("image_startdaemon").set_from_stock( gtk.STOCK_EXECUTE, gtk.ICON_SIZE_MENU) self.glade.get_widget("label_startdaemon").set_text( _("_Start Daemon")) if not localhost: # An offline host self.glade.get_widget("button_startdaemon").set_sensitive(False) # Make sure label is displayed correctly using mnemonics self.glade.get_widget("label_startdaemon").set_use_underline( True)
options["stop_at_ratio"] = state.stop_at_ratio options["stop_ratio"] = state.stop_ratio options["remove_at_ratio"] = state.remove_at_ratio options["move_completed"] = state.move_completed options["move_completed_path"] = state.move_completed_path options["add_paused"] = state.paused ti = self.get_torrent_info_from_file( os.path.join(get_config_dir(), "state", state.torrent_id + ".torrent")) if ti: add_torrent_params["ti"] = ti elif state.magnet: magnet = state.magnet else: log.error("Unable to add torrent!") return # Handle legacy case with storing resume data in individual files # for each torrent if resume_data is None: resume_data = self.legacy_get_resume_data_from_file(state.torrent_id) self.legacy_delete_resume_data(state.torrent_id) if resume_data: add_torrent_params["resume_data"] = resume_data else: # We have a torrent_info object or magnet uri so we're not loading from state. if torrent_info: add_torrent_id = str(torrent_info.info_hash()) # If this torrent id is already in the session, merge any additional trackers.
def disable(self): log.debug("Disabling Web UI notifications")
def __init__(self, args=None): component.Component.__init__(self, "ConsoleUI", 2) self.batch_write = False try: locale.setlocale(locale.LC_ALL, '') self.encoding = locale.getpreferredencoding() except: self.encoding = sys.getdefaultencoding() log.debug("Using encoding: %s", self.encoding) # Load all the commands self._commands = load_commands(os.path.join(UI_PATH, 'commands')) client.set_disconnect_callback(self.on_client_disconnect) # Set the interactive flag to indicate where we should print the output self.interactive = True if args: # Multiple commands split by ";" commands = [arg.strip() for arg in ' '.join(args).split(';')] self.interactive = False # Try to connect to the daemon (localhost by default) def on_connect(result): def on_started(result): if not self.interactive: def on_started(result): def do_command(result, cmd): return self.do_command(cmd) d = defer.succeed(None) # If we have commands, lets process them, then quit. for command in commands: d.addCallback(do_command, command) if "quit" not in commands and "exit" not in commands: d.addCallback(do_command, "quit") # We need to wait for the rpcs in start() to finish before processing # any of the commands. self.started_deferred.addCallback(on_started) component.start().addCallback(on_started) def on_connect_fail(result): if not self.interactive: self.do_command('quit') connect_cmd = 'connect' if not self.interactive: if commands[0].startswith(connect_cmd): connect_cmd = commands.pop(0) elif 'help' in commands: self.do_command('help') return d = self.do_command(connect_cmd) d.addCallback(on_connect) d.addErrback(on_connect_fail) self.coreconfig = CoreConfig() if self.interactive and not deluge.common.windows_check(): # We use the curses.wrapper function to prevent the console from getting # messed up if an uncaught exception is experienced. import curses.wrapper curses.wrapper(self.run) elif self.interactive and deluge.common.windows_check(): print """\nDeluge-console does not run in interactive mode on Windows. \n Please use commands from the command line, eg:\n deluge-console.exe help deluge-console.exe info deluge-console.exe "add --help" deluge-console.exe "add -p c:\\mytorrents c:\\new.torrent" """ else: reactor.run()