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, 13 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