diff --git a/ChangeLog b/ChangeLog index ba4c8133b..b1062d393 100644 --- a/ChangeLog +++ b/ChangeLog @@ -51,6 +51,7 @@ * #2201: Fix error in authmanager if auth file has extra newlines * #2109: Fix the Proxy settings not being cleared by setting None * #2110: Fix accepting magnet uris with xt param anywhere within them + * #2204: Fix daemon shutdown hang ==== GtkUI ==== * Add move completed option to add torrent dialog diff --git a/deluge/_libtorrent.py b/deluge/_libtorrent.py index ab782d67a..898cfb150 100644 --- a/deluge/_libtorrent.py +++ b/deluge/_libtorrent.py @@ -45,9 +45,9 @@ supports. """ -REQUIRED_VERSION = "0.14.9.0" +REQUIRED_VERSION = "0.16.1.0" -def check_version(LT): +def check_version(lt): from deluge.common import VersionSplit if VersionSplit(lt.version) < VersionSplit(REQUIRED_VERSION): raise ImportError("This version of Deluge requires libtorrent >=%s!" % REQUIRED_VERSION) diff --git a/deluge/component.py b/deluge/component.py index 538d2f9f6..6085db277 100644 --- a/deluge/component.py +++ b/deluge/component.py @@ -34,6 +34,7 @@ # import logging +from collections import defaultdict from twisted.internet.defer import maybeDeferred, succeed, DeferredList, fail from twisted.internet.task import LoopingCall @@ -225,6 +226,8 @@ class ComponentRegistry(object): """ def __init__(self): self.components = {} + # Stores all of the components that are dependent on a particular component + self.dependents = defaultdict(list) def register(self, obj): """ @@ -243,6 +246,9 @@ class ComponentRegistry(object): "Component already registered with name %s" % name) self.components[obj._component_name] = obj + if obj._component_depend: + for depend in obj._component_depend: + self.dependents[depend].append(name) def deregister(self, obj): """ @@ -317,11 +323,23 @@ class ComponentRegistry(object): elif isinstance(names, str): names = [names] + def on_dependents_stopped(result, name): + return self.components[name]._component_stop() + + stopped_in_deferred = set() deferreds = [] for name in names: + if name in stopped_in_deferred: + continue if name in self.components: - deferreds.append(self.components[name]._component_stop()) + if name in self.dependents: + # If other components depend on this component, stop them first + d = self.stop(self.dependents[name]).addCallback(on_dependents_stopped, name) + deferreds.append(d) + stopped_in_deferred.update(self.dependents[name]) + else: + deferreds.append(self.components[name]._component_stop()) return DeferredList(deferreds) @@ -360,7 +378,7 @@ class ComponentRegistry(object): :param names: a list of Components to resume :type names: list - :returns: a Deferred object that will fire once all Components have been sucessfully resumed + :returns: a Deferred object that will fire once all Components have been successfully resumed :rtype: twisted.internet.defer.Deferred """ @@ -384,16 +402,14 @@ class ComponentRegistry(object): be called when the program is exiting to ensure all Components have a chance to properly shutdown. - :returns: a Deferred object that will fire once all Components have been sucessfully resumed + :returns: a Deferred object that will fire once all Components have been successfully shut down :rtype: twisted.internet.defer.Deferred """ - deferreds = [] + def on_stopped(result): + return DeferredList(map(lambda c: c._component_shutdown(), self.components.values())) - for component in self.components.values(): - deferreds.append(component._component_shutdown()) - - return DeferredList(deferreds) + return self.stop(self.components.keys()).addCallback(on_stopped) def update(self): """ diff --git a/deluge/core/alertmanager.py b/deluge/core/alertmanager.py index e2ff6c865..063bda723 100644 --- a/deluge/core/alertmanager.py +++ b/deluge/core/alertmanager.py @@ -75,7 +75,8 @@ class AlertManager(component.Component): def stop(self): for dc in self.delayed_calls: - dc.cancel() + if dc.active(): + dc.cancel() self.delayed_calls = [] def register_handler(self, alert_type, handler): diff --git a/deluge/core/core.py b/deluge/core/core.py index e69944790..242359a9d 100644 --- a/deluge/core/core.py +++ b/deluge/core/core.py @@ -94,6 +94,8 @@ class Core(component.Component): self.settings.user_agent = "Deluge/%(deluge_version)s Libtorrent/%(lt_version)s" % \ { 'deluge_version': deluge.common.get_version(), 'lt_version': self.get_libtorrent_version().rpartition(".")[0] } + # Increase the alert queue size so that alerts don't get lost + self.settings.alert_queue_size = 10000 # Set session settings self.settings.send_redundant_have = True diff --git a/deluge/core/torrent.py b/deluge/core/torrent.py index f90a7b5e7..ba6ec285a 100644 --- a/deluge/core/torrent.py +++ b/deluge/core/torrent.py @@ -37,9 +37,12 @@ import os import time import logging +import re from urllib import unquote from urlparse import urlparse +from twisted.internet.defer import Deferred, DeferredList +from twisted.internet.task import LoopingCall from deluge._libtorrent import lt import deluge.common @@ -116,7 +119,6 @@ class Torrent(object): # We use this to return dicts that only contain changes from the previous # {session_id: status_dict, ...} self.prev_status = {} - from twisted.internet.task import LoopingCall self.prev_status_cleanup_loop = LoopingCall(self.cleanup_prev_status) self.prev_status_cleanup_loop.start(10) @@ -125,14 +127,10 @@ class Torrent(object): # Set the torrent_id for this torrent self.torrent_id = str(handle.info_hash()) - # Let's us know if we're waiting on a lt alert - self.waiting_on_resume_data = False - - # Keep a list of file indexes we're waiting for file_rename alerts on - # This also includes the old_folder and new_folder to know what signal to send + # Keep a list of Deferreds for file indexes we're waiting for file_rename alerts on # This is so we can send one folder_renamed signal instead of multiple # file_renamed signals. - # [(old_folder, new_folder, [*indexes]), ...] + # [{index: Deferred, ...}, ...] self.waiting_on_folder_rename = [] # We store the filename just in case we need to make a copy of the torrentfile @@ -177,13 +175,7 @@ class Torrent(object): # Tracker list self.trackers = [] # Create a list of trackers - for value in self.handle.trackers(): - if lt.version_minor < 15: - tracker = {} - tracker["url"] = value.url - tracker["tier"] = value.tier - else: - tracker = value + for tracker in self.handle.trackers(): self.trackers.append(tracker) # Various torrent options @@ -919,7 +911,6 @@ class Torrent(object): """Signals libtorrent to build resume data for this torrent, it gets returned in a libtorrent alert""" self.handle.save_resume_data() - self.waiting_on_resume_data = True def write_torrentfile(self): """Writes the torrent file""" @@ -999,8 +990,13 @@ class Torrent(object): self.handle.rename_file(index, filename.encode("utf-8")) def rename_folder(self, folder, new_folder): - """Renames a folder within a torrent. This basically does a file rename - on all of the folders children.""" + """ + Renames a folder within a torrent. This basically does a file rename + on all of the folders children. + + :returns: A deferred which fires when the rename is complete + :rtype: twisted.internet.defer.Deferred + """ log.debug("attempting to rename folder: %s to %s", folder, new_folder) if len(new_folder) < 1: log.error("Attempting to rename a folder with an invalid folder name: %s", new_folder) @@ -1008,13 +1004,57 @@ class Torrent(object): new_folder = sanitize_filepath(new_folder, folder=True) - wait_on_folder = (folder, new_folder, []) + def on_file_rename_complete(result, wait_dict, index): + wait_dict.pop(index, None) + + wait_on_folder = {} + self.waiting_on_folder_rename.append(wait_on_folder) for f in self.get_files(): if f["path"].startswith(folder): - # Keep a list of filerenames we're waiting on - wait_on_folder[2].append(f["index"]) + # Keep track of filerenames we're waiting on + wait_on_folder[f["index"]] = Deferred().addBoth(on_file_rename_complete, wait_on_folder, f["index"]) self.handle.rename_file(f["index"], f["path"].replace(folder, new_folder, 1).encode("utf-8")) - self.waiting_on_folder_rename.append(wait_on_folder) + + def on_folder_rename_complete(result, torrent, folder, new_folder): + component.get("EventManager").emit(TorrentFolderRenamedEvent(torrent.torrent_id, folder, new_folder)) + # Empty folders are removed after libtorrent folder renames + self.remove_empty_folders(folder) + torrent.waiting_on_folder_rename = filter(None, torrent.waiting_on_folder_rename) + component.get("TorrentManager").save_resume_data((self.torrent_id,)) + + d = DeferredList(wait_on_folder.values()) + d.addBoth(on_folder_rename_complete, self, folder, new_folder) + return d + + def remove_empty_folders(self, folder): + """ + Recursively removes folders but only if they are empty. + Cleans up after libtorrent folder renames. + + """ + info = self.get_status(['save_path']) + # Regex removes leading slashes that causes join function to ignore save_path + folder_full_path = os.path.join(info['save_path'], re.sub("^/*", "", folder)) + folder_full_path = os.path.normpath(folder_full_path) + + try: + if not os.listdir(folder_full_path): + os.removedirs(folder_full_path) + log.debug("Removed Empty Folder %s", folder_full_path) + else: + for root, dirs, files in os.walk(folder_full_path, topdown=False): + for name in dirs: + try: + os.removedirs(os.path.join(root, name)) + log.debug("Removed Empty Folder %s", os.path.join(root, name)) + except OSError as (errno, strerror): + from errno import ENOTEMPTY + if errno == ENOTEMPTY: + # Error raised if folder is not empty + log.debug("%s", strerror) + + except OSError as (errno, strerror): + log.debug("Cannot Remove Folder: %s (ErrNo %s)", strerror, errno) def cleanup_prev_status(self): """ diff --git a/deluge/core/torrentmanager.py b/deluge/core/torrentmanager.py index a985d531c..d60d78575 100644 --- a/deluge/core/torrentmanager.py +++ b/deluge/core/torrentmanager.py @@ -38,13 +38,13 @@ import cPickle import os -import time import shutil import operator import logging -import re +from collections import defaultdict from twisted.internet.task import LoopingCall +from twisted.internet.defer import Deferred, DeferredList from deluge._libtorrent import lt @@ -133,7 +133,7 @@ class TorrentManager(component.Component): def __init__(self): component.Component.__init__(self, "TorrentManager", interval=5, - depend=["CorePluginManager"]) + depend=["CorePluginManager", "AlertManager"]) log.debug("TorrentManager init..") # Set the libtorrent session self.session = component.get("Core").session @@ -151,15 +151,11 @@ class TorrentManager(component.Component): self.last_seen_complete_loop = None 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 = [] + # This is a map of torrent_ids to Deferreds used to track needed resume data. + # The Deferreds will be completed when resume data has been saved. + self.waiting_on_resume_data = defaultdict(list) - # self.num_resume_data used to save resume_data in bulk - self.num_resume_data = 0 - - # Keeps track of resume data that needs to be saved to disk + # Keeps track of resume data self.resume_data = {} # Register set functions @@ -216,11 +212,14 @@ class TorrentManager(component.Component): # Try to load the state from file self.load_state() - # Save the state every 5 minutes + # Save the state periodically self.save_state_timer = LoopingCall(self.save_state) self.save_state_timer.start(200, False) self.save_resume_data_timer = LoopingCall(self.save_resume_data) - self.save_resume_data_timer.start(190) + self.save_resume_data_timer.start(190, False) + # Force update for all resume data a bit less frequently + self.save_all_resume_data_timer = LoopingCall(self.save_resume_data, self.torrents.keys()) + self.save_all_resume_data_timer.start(900, False) if self.last_seen_complete_loop: self.last_seen_complete_loop.start(60) @@ -233,45 +232,21 @@ class TorrentManager(component.Component): if self.save_resume_data_timer.running: self.save_resume_data_timer.stop() + if self.save_all_resume_data_timer.running: + self.save_all_resume_data_timer.stop() + if self.last_seen_complete_loop: self.last_seen_complete_loop.stop() # Save state on shutdown self.save_state() - # Make another list just to make sure all paused torrents will be - # passed to self.save_resume_data(). With - # self.shutdown_torrent_pause_list it is possible to have a case when - # torrent_id is removed from it in self.on_alert_torrent_paused() - # before we call self.save_resume_data() here. - save_resume_data_list = [] + self.session.pause() for key in self.torrents: # Stop the status cleanup LoopingCall here self.torrents[key].prev_status_cleanup_loop.stop() - if not self.torrents[key].handle.is_paused(): - # We set auto_managed false to prevent lt from resuming the torrent - self.torrents[key].handle.auto_managed(False) - self.torrents[key].handle.pause() - self.shutdown_torrent_pause_list.append(key) - save_resume_data_list.append(key) - self.save_resume_data(save_resume_data_list) - - # We have to wait for all torrents to pause and write their resume data - wait = True - while wait: - if self.shutdown_torrent_pause_list: - wait = True - else: - wait = False - for torrent in self.torrents.values(): - if torrent.waiting_on_resume_data: - wait = True - break - - time.sleep(0.01) - # Wait for all alerts - self.alerts.handle_alerts(True) + return self.save_resume_data(self.torrents.keys()) def update(self): for torrent_id, torrent in self.torrents.items(): @@ -613,9 +588,7 @@ class TorrentManager(component.Component): return False # Remove fastresume data if it is exists - resume_data = self.load_resume_data_file() - resume_data.pop(torrent_id, None) - self.save_resume_data_file(resume_data) + self.resume_data.pop(torrent_id, None) # Remove the .torrent file in the state self.torrents[torrent_id].delete_torrentfile() @@ -777,17 +750,32 @@ class TorrentManager(component.Component): def save_resume_data(self, torrent_ids=None): """ - Saves resume data for list of torrent_ids or for all torrents if - torrent_ids is None + Saves resume data for list of torrent_ids or for all torrents + needing resume data updated if torrent_ids is None + + :returns: A Deferred whose callback will be invoked when save is complete + :rtype: twisted.internet.defer.Deferred """ if torrent_ids is None: - torrent_ids = self.torrents.keys() + torrent_ids = (t[0] for t in self.torrents.iteritems() if t[1].handle.need_save_resume_data()) + + deferreds = [] + + def on_torrent_resume_save(result, torrent_id): + self.waiting_on_resume_data.pop(torrent_id, None) for torrent_id in torrent_ids: + d = Deferred().addBoth(on_torrent_resume_save, torrent_id) + self.waiting_on_resume_data[torrent_id].append(d) + deferreds.append(d) self.torrents[torrent_id].save_resume_data() - self.num_resume_data = len(torrent_ids) + def on_all_resume_data_finished(result): + if result: + self.save_resume_data_file() + + return DeferredList(deferreds).addBoth(on_all_resume_data_finished) def load_resume_data_file(self): resume_data = {} @@ -807,73 +795,22 @@ class TorrentManager(component.Component): return resume_data - def save_resume_data_file(self, resume_data=None): + def save_resume_data_file(self): """ - 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 - + Saves the resume data file with the contents of self.resume_data. """ - # 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.write(lt.bencode(self.resume_data)) fastresume_file.flush() os.fsync(fastresume_file.fileno()) fastresume_file.close() except IOError: log.warning("Error trying to save fastresume file") - def remove_empty_folders(self, torrent_id, folder): - """ - Recursively removes folders but only if they are empty. - Cleans up after libtorrent folder renames. - - """ - if torrent_id not in self.torrents: - raise InvalidTorrentError("torrent_id is not in session") - - info = self.torrents[torrent_id].get_status(['save_path']) - # Regex removes leading slashes that causes join function to ignore save_path - folder_full_path = os.path.join(info['save_path'], re.sub("^/*", "", folder)) - folder_full_path = os.path.normpath(folder_full_path) - - try: - if not os.listdir(folder_full_path): - os.removedirs(folder_full_path) - log.debug("Removed Empty Folder %s", folder_full_path) - else: - for root, dirs, files in os.walk(folder_full_path, topdown=False): - for name in dirs: - try: - os.removedirs(os.path.join(root, name)) - log.debug("Removed Empty Folder %s", os.path.join(root, name)) - except OSError as (errno, strerror): - from errno import ENOTEMPTY - if errno == ENOTEMPTY: - # Error raised if folder is not empty - log.debug("%s", strerror) - - except OSError as (errno, strerror): - log.debug("Cannot Remove Folder: %s (ErrNo %s)", strerror, errno) - def get_queue_position(self, torrent_id): """Get queue position of torrent""" return self.torrents[torrent_id].get_queue_position() @@ -988,14 +925,9 @@ class TorrentManager(component.Component): if torrent.state != old_state: component.get("EventManager").emit(TorrentStateChangedEvent(torrent_id, torrent.state)) - # Don't save resume data for each torrent after self.stop() was called. - # We save resume data in bulk in self.stop() in this case. - if self.save_resume_data_timer.running: - # Write the fastresume file - self.save_resume_data((torrent_id, )) - - if torrent_id in self.shutdown_torrent_pause_list: - self.shutdown_torrent_pause_list.remove(torrent_id) + # Write the fastresume file if we are not waiting on a bulk write + if torrent_id not in self.waiting_on_resume_data: + self.save_resume_data((torrent_id,)) def on_alert_torrent_checked(self, alert): log.debug("on_alert_torrent_checked") @@ -1105,32 +1037,22 @@ class TorrentManager(component.Component): def on_alert_save_resume_data(self, alert): log.debug("on_alert_save_resume_data") - try: - torrent_id = str(alert.handle.info_hash()) - torrent = self.torrents[torrent_id] - except: - return + torrent_id = str(alert.handle.info_hash()) - # Libtorrent in add_torrent() expects resume_data to be bencoded - self.resume_data[torrent_id] = lt.bencode(alert.resume_data) - self.num_resume_data -= 1 + if torrent_id in self.torrents: + # Libtorrent in add_torrent() expects resume_data to be bencoded + self.resume_data[torrent_id] = lt.bencode(alert.resume_data) - torrent.waiting_on_resume_data = False - - self.save_resume_data_file() + if torrent_id in self.waiting_on_resume_data: + for d in self.waiting_on_resume_data[torrent_id]: + d.callback(None) def on_alert_save_resume_data_failed(self, alert): log.debug("on_alert_save_resume_data_failed: %s", alert.message()) - try: - torrent = self.torrents[str(alert.handle.info_hash())] - except: - return - - self.num_resume_data -= 1 - torrent.waiting_on_resume_data = False - - self.save_resume_data_file() + torrent_id = alert.handle.info_hash() + if torrent_id in self.waiting_on_resume_data: + self.waiting_on_resume_data[torrent_id].errback(Exception(alert.message())) def on_alert_file_renamed(self, alert): log.debug("on_alert_file_renamed") @@ -1141,24 +1063,12 @@ class TorrentManager(component.Component): 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: + # We need to see if this file index is in a waiting_on_folder dict + for wait_on_folder in torrent.waiting_on_folder_rename: + if alert.index in wait_on_folder: + wait_on_folder[alert.index].callback(None) + break + else: # 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,)) diff --git a/deluge/ui/gtkui/addtorrentdialog.py b/deluge/ui/gtkui/addtorrentdialog.py index 84ea16e57..e2df0c91d 100644 --- a/deluge/ui/gtkui/addtorrentdialog.py +++ b/deluge/ui/gtkui/addtorrentdialog.py @@ -825,14 +825,15 @@ class AddTorrentDialog(component.Component): self.save_torrent_options(row) - # The options we want all the torrents to have - options = self.options[model.get_value(row, 0)] + # The options, except file renames, we want all the torrents to have + options = self.options[model.get_value(row, 0)].copy() + del options["mapped_files"] # Set all the torrent options row = model.get_iter_first() while row != None: torrent_id = model.get_value(row, 0) - self.options[torrent_id] = options + self.options[torrent_id].update(options) row = model.iter_next(row) def _on_button_revert_clicked(self, widget): @@ -863,7 +864,7 @@ class AddTorrentDialog(component.Component): def _on_filename_edited(self, renderer, path, new_text): index = self.files_treestore[path][3] - new_text = new_text.strip(os.path.sep) + new_text = new_text.strip(os.path.sep).strip() # Return if the text hasn't changed if new_text == self.files_treestore[path][1]: @@ -881,9 +882,14 @@ class AddTorrentDialog(component.Component): if index > -1: # We're renaming a file! Yay! That's easy! + if not new_text: + return parent = self.files_treestore.iter_parent(itr) file_path = os.path.join(self.get_file_path(parent), new_text) - + # Don't rename if filename exists + for row in self.files_treestore[parent].iterchildren(): + if new_text == row[1]: + return if os.path.sep in new_text: # There are folders in this path, so we need to create them # and then move the file iter to top diff --git a/deluge/ui/web/js/deluge-all/add/FilesTab.js b/deluge/ui/web/js/deluge-all/add/FilesTab.js index 3e624823d..709105b1b 100644 --- a/deluge/ui/web/js/deluge-all/add/FilesTab.js +++ b/deluge/ui/web/js/deluge-all/add/FilesTab.js @@ -1,6 +1,6 @@ /*! * Deluge.add.FilesTab.js - * + * * Copyright (c) Damien Churchill 2009-2010 * * This program is free software; you can redistribute it and/or modify @@ -40,7 +40,7 @@ Deluge.add.FilesTab = Ext.extend(Ext.ux.tree.TreeGrid, { layout: 'fit', title: _('Files'), - autoScroll: true, + autoScroll: false, animate: false, border: false, disabled: true, diff --git a/deluge/ui/web/js/deluge-all/add/OptionsTab.js b/deluge/ui/web/js/deluge-all/add/OptionsTab.js index c65dc6f40..9ea306c73 100644 --- a/deluge/ui/web/js/deluge-all/add/OptionsTab.js +++ b/deluge/ui/web/js/deluge-all/add/OptionsTab.js @@ -1,6 +1,6 @@ /*! * Deluge.add.OptionsPanel.js - * + * * Copyright (c) Damien Churchill 2009-2010 * * This program is free software; you can redistribute it and/or modify @@ -67,7 +67,7 @@ Deluge.add.OptionsTab = Ext.extend(Ext.form.FormPanel, { width: 400, labelSeparator: '' })); - + var panel = this.add({ border: false, layout: 'column', @@ -78,7 +78,6 @@ Deluge.add.OptionsTab = Ext.extend(Ext.form.FormPanel, { border: false, autoHeight: true, defaultType: 'radio', - width: 100 }); this.optionsManager.bind('compact_allocation', fieldset.add({ @@ -86,6 +85,7 @@ Deluge.add.OptionsTab = Ext.extend(Ext.form.FormPanel, { columns: 1, vertical: true, labelSeparator: '', + width: 80, items: [{ name: 'compact_allocation', value: false, @@ -107,35 +107,32 @@ Deluge.add.OptionsTab = Ext.extend(Ext.form.FormPanel, { title: _('Bandwidth'), border: false, autoHeight: true, - labelWidth: 100, + bodyStyle: 'margin-left: 7px', + labelWidth: 105, width: 200, defaultType: 'spinnerfield' }); this.optionsManager.bind('max_download_speed', fieldset.add({ fieldLabel: _('Max Down Speed'), - labelStyle: 'margin-left: 10px', name: 'max_download_speed', width: 60 })); this.optionsManager.bind('max_upload_speed', fieldset.add({ fieldLabel: _('Max Up Speed'), - labelStyle: 'margin-left: 10px', name: 'max_upload_speed', width: 60 })); this.optionsManager.bind('max_connections', fieldset.add({ fieldLabel: _('Max Connections'), - labelStyle: 'margin-left: 10px', name: 'max_connections', width: 60 })); this.optionsManager.bind('max_upload_slots', fieldset.add({ fieldLabel: _('Max Upload Slots'), - labelStyle: 'margin-left: 10px', name: 'max_upload_slots', width: 60 })); - + fieldset = panel.add({ title: _('General'), border: false, diff --git a/setup.py b/setup.py index a55567c76..a649755b3 100755 --- a/setup.py +++ b/setup.py @@ -152,11 +152,12 @@ else: for include in os.environ.get("INCLUDEDIR", "").split(":"): _include_dirs.append(include) - _library_dirs += [sysconfig.get_config_var("LIBDIR"), '/opt/local/lib'] + _library_dirs += [sysconfig.get_config_var("LIBDIR"), '/opt/local/lib', '/usr/local/lib'] if osx_check(): _include_dirs += [ '/opt/local/include/boost-1_35', '/opt/local/include/boost-1_36', + '/usr/local/include' '/sw/include/boost-1_35', '/sw/include/boost' ]