Ticket #1858: 0001-Implement-console-command-for-displaying-and-setting.2.patch

File 0001-Implement-console-command-for-displaying-and-setting.2.patch, 9.6 KB (added by eirikba, 12 years ago)
  • new file deluge/ui/console/commands/manage.py

    From e071382a7024e170fd4996f8e7ca6fa278d86176 Mon Sep 17 00:00:00 2001
    From: Eirik Byrkjeflot Anonsen <eirik@eirikba.org>
    Date: Thu, 19 May 2011 21:19:13 +0200
    Subject: [PATCH] Implement console command for displaying and setting per-torrent options.
    
    ---
     deluge/ui/console/commands/manage.py |  208 ++++++++++++++++++++++++++++++++++
     1 files changed, 208 insertions(+), 0 deletions(-)
     create mode 100644 deluge/ui/console/commands/manage.py
    
    diff --git a/deluge/ui/console/commands/manage.py b/deluge/ui/console/commands/manage.py
    new file mode 100644
    index 0000000..6e7bbfc
    - +  
     1# 
     2# manage.py 
     3# 
     4# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com> 
     5# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com> 
     6# 
     7# Deluge is free software. 
     8# 
     9# You may redistribute it and/or modify it under the terms of the 
     10# GNU General Public License, as published by the Free Software 
     11# Foundation; either version 3 of the License, or (at your option) 
     12# any later version. 
     13# 
     14# deluge is distributed in the hope that it will be useful, 
     15# but WITHOUT ANY WARRANTY; without even the implied warranty of 
     16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
     17# See the GNU General Public License for more details. 
     18# 
     19# You should have received a copy of the GNU General Public License 
     20# along with deluge.    If not, write to: 
     21#       The Free Software Foundation, Inc., 
     22#       51 Franklin Street, Fifth Floor 
     23#       Boston, MA  02110-1301, USA. 
     24# 
     25#    In addition, as a special exception, the copyright holders give 
     26#    permission to link the code of portions of this program with the OpenSSL 
     27#    library. 
     28#    You must obey the GNU General Public License in all respects for all of 
     29#    the code used other than OpenSSL. If you modify file(s) with this 
     30#    exception, you may extend this exception to your version of the file(s), 
     31#    but you are not obligated to do so. If you do not wish to do so, delete 
     32#    this exception statement from your version. If you delete this exception 
     33#    statement from all source files in the program, then also delete it here. 
     34# 
     35# 
     36 
     37from twisted.internet import defer 
     38 
     39from deluge.ui.console.main import BaseCommand 
     40import deluge.ui.console.colors as colors 
     41from deluge.ui.client import client 
     42import deluge.component as component 
     43from deluge.log import LOG as log 
     44 
     45from optparse import make_option 
     46 
     47 
     48# torrent.py's Torrent.get_status() renames some of the options. 
     49# This maps the getter names to the setter names 
     50get_torrent_options = { 
     51    'max_download_speed': 'max_download_speed', 
     52    'max_upload_speed': 'max_upload_speed', 
     53    'max_connections': 'max_connections', 
     54    'max_upload_slots': 'max_upload_slots', 
     55    'prioritize_first_last': 'prioritize_first_last_pieces', 
     56    'is_auto_managed': 'auto_managed', 
     57    'stop_at_ratio': 'stop_at_ratio', 
     58    'stop_ratio': 'stop_ratio', 
     59    'remove_at_ratio': 'remove_at_ratio', 
     60    'move_on_completed': 'move_completed', 
     61    'move_on_completed_path': 'move_completed_path', 
     62    #'file_priorities': 'file_priorities', 
     63    #'compact': 'compact_allocation', 
     64    #'save_path': 'download_location' 
     65    } 
     66 
     67# These are the things that can be set, as far as I can tell.  The 
     68# ones that aren't commented out are the ones that look like they 
     69# probably will behave as expected if set. 
     70# 
     71# Each value is [ type, getter name, help text ] 
     72set_torrent_options = { 
     73    # These are handled by torrent.py's Torrent.set_options() 
     74    'auto_managed': [bool, None, '(Actually, I do not know what this does)'], 
     75    #'download_location': str, # Probably not useful to set 
     76    #'file_priorities': ???, # Not a simple value to set, probably needs its own command 
     77    'max_connections': [int, None, 'Maximum number of connections to use for this torrent'], 
     78    'max_download_speed': [float, None, 'Maximum total download speed to allow this torrent to use (KiB/s)'], 
     79    'max_upload_slots': [int, None, 'Maximum number of connections to use for uploading for this torrent'], 
     80    'max_upload_speed': [float, None, 'Maximum total upload speed to allow this torrent to use (KiB/s)'], 
     81    'prioritize_first_last_pieces': [bool, None, 'Whether to download the first and last piece of the torrent first.  NOTE: Only has effect if the torrent contains a single file'], # Only has effect if torrent contains a single file 
     82 
     83    # These are "handled" by being set directly on the options dict 
     84    'stop_at_ratio': [bool, None, 'Whether to stop seeding when share ratio reaches "stop_ratio".'], 
     85    'stop_ratio': [float, None, 'The ratio at which to stop seeding (if "stop_at_ratio" is True)'], 
     86    'remove_at_ratio': [bool, None, 'Whether to remove torrent when share ratio reaches "stop_ratio".'], 
     87    'move_completed': [bool, None, 'Whether to move the downloaded data when downloading is complete.'], 
     88    'move_completed_path': [str, None, 'Where to move completed data to (if "move_completed" is True)'], 
     89 
     90    #'compact_allocation': bool, # Unclear what setting this would do 
     91    #'add_paused': ???, # Not returned by get_status, unclear what setting it would do 
     92    #'mapped_files': ??? # Not returned by get_status, unclear what setting it would do 
     93    } 
     94for k,v in get_torrent_options.items(): 
     95    set_torrent_options[v][1] = k 
     96 
     97 
     98class Command(BaseCommand): 
     99    """Show and set per-torrent options""" 
     100 
     101    option_help = [ ' ' * 7 + k + ':  ' + v[2] for k,v in set_torrent_options.items() ] 
     102    option_help.sort() 
     103 
     104    option_list = BaseCommand.option_list + ( 
     105            make_option('-s', '--set', action='store', nargs=2, dest='set', 
     106                        help='set value for key'), 
     107    ) 
     108    usage = '''Usage: manage <torrent-id> [<torrent-id> ...] [<key1> [<key2> ...]] 
     109       manage <torrent-id> [<torrent-id> ...] --set <key> <value> 
     110 
     111       torrent-id can be "*" to signify "all torrents" 
     112 
     113Possible options that this command can display or show: 
     114''' + '\n'.join(option_help) 
     115 
     116 
     117    def handle(self, *args, **options): 
     118        self.console = component.get('ConsoleUI') 
     119        if options['set']: 
     120            return self._set_option(*args, **options) 
     121        else: 
     122            return self._get_option(*args, **options) 
     123 
     124 
     125    def _get_option(self, *args, **options): 
     126 
     127        def on_torrents_status(status): 
     128            for torrentid, data in status.items(): 
     129                self.console.write('\n') 
     130                if 'name' in data: 
     131                    self.console.write('{!info!}Name: {!input!}%s' % data.get('name')) 
     132                self.console.write('{!info!}ID: {!input!}%s' % torrentid) 
     133                for k, v in data.items(): 
     134                    if k != 'name': 
     135                        displayname = get_torrent_options.get(k, '???' + k) 
     136                        self.console.write('{!info!}%s: {!input!}%s' % (displayname, v)) 
     137 
     138        def on_torrents_status_fail(reason): 
     139            self.console.write('{!error!}Failed to get torrent data.') 
     140 
     141        torrent_ids = [] 
     142        request_options = [] 
     143 
     144        for arg in args: 
     145            if arg in set_torrent_options: 
     146                request_options.append(set_torrent_options[arg][1]) 
     147            else: 
     148                if arg == '*': 
     149                    ids = self.console.match_torrent('') 
     150                else: 
     151                    ids = self.console.match_torrent(arg) 
     152                if not ids: 
     153                    self.console.write("{!error!}The argument '" + arg + "' is not a recognized option nor did it match any torrents") 
     154                    return 
     155                torrent_ids.extend(ids) 
     156 
     157        if not torrent_ids: 
     158            self.console.write('{!error!}No torrents mentioned.  To request info on all torrents use "manage * [<key>...]".') 
     159            return 
     160 
     161        if not request_options: 
     162            request_options = [ opt for opt in get_torrent_options ] 
     163        request_options.append('name') 
     164 
     165        d = client.core.get_torrents_status({'id': torrent_ids}, request_options) 
     166        d.addCallback(on_torrents_status) 
     167        d.addErrback(on_torrents_status_fail) 
     168        return d 
     169 
     170 
     171    def _set_option(self, *args, **options): 
     172        deferred = defer.Deferred() 
     173        torrent_ids = [] 
     174        for arg in args: 
     175            if arg == '*': 
     176                ids = self.console.match_torrent('') 
     177            else: 
     178                ids = self.console.match_torrent(arg) 
     179            if not ids: 
     180                self.console.write("{!error!}The argument '" + arg + "' did not match any torrents") 
     181                return 
     182            torrent_ids.extend(ids) 
     183        if not torrent_ids: 
     184            self.console.write('{!error!}No torrents mentioned.') 
     185            return 
     186        key = options['set'][0] 
     187        val = options['set'][1] 
     188 
     189        if key not in set_torrent_options: 
     190            self.console.write("{!error!}The key '%s' is invalid!" % key) 
     191            return 
     192 
     193        val = set_torrent_options[key][0](val) 
     194 
     195        def on_set_config(result): 
     196            self.console.write('{!success!}Torrent option successfully updated.') 
     197            deferred.callback(True) 
     198 
     199        torrent_names = [ self.console.get_torrent_name(tid) for tid in torrent_ids ] 
     200        self.console.write('{!info!}Setting %s to %s for torrent(s): %s' % (key, val, ', '.join(torrent_names))) 
     201        client.core.set_torrent_options(torrent_ids, {key: val}).addCallback(on_set_config) 
     202        return deferred 
     203 
     204    def complete(self, text): 
     205        torrents = component.get('ConsoleUI').tab_complete_torrent(text) 
     206        options = [ x for x in set_torrent_options if x.startswith(text) ] 
     207        # This should probably only return options immediately after --set. 
     208        return torrents + options