| 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 | |
| 37 | from twisted.internet import defer |
| 38 | |
| 39 | from deluge.ui.console.main import BaseCommand |
| 40 | import deluge.ui.console.colors as colors |
| 41 | from deluge.ui.client import client |
| 42 | import deluge.component as component |
| 43 | from deluge.log import LOG as log |
| 44 | |
| 45 | from optparse import make_option |
| 46 | |
| 47 | torrent_options = { |
| 48 | 'max_download_speed': float, |
| 49 | 'max_upload_speed': float, |
| 50 | 'max_connections': int, |
| 51 | 'max_upload_slots': int, |
| 52 | 'private': bool, |
| 53 | 'prioritize_first_last': bool, |
| 54 | 'is_auto_managed': bool, |
| 55 | 'stop_at_ratio': bool, |
| 56 | 'stop_ratio': float, |
| 57 | 'remove_at_ratio': bool, |
| 58 | 'move_on_completed': bool, |
| 59 | 'move_on_completed_path': str |
| 60 | } |
| 61 | |
| 62 | |
| 63 | class Command(BaseCommand): |
| 64 | """Show and set per-torrent options""" |
| 65 | |
| 66 | option_list = BaseCommand.option_list + ( |
| 67 | make_option('-s', '--set', action='store', nargs=2, dest='set', |
| 68 | help='set value for key'), |
| 69 | ) |
| 70 | usage = 'Usage: manage [<torrent-id> [<torrent-id> ...]] [<key1> [<key2> ...]]\n'\ |
| 71 | ' manage <torrent-id> [<torrent-id> ...] --set <key> <value>' |
| 72 | |
| 73 | def handle(self, *args, **options): |
| 74 | self.console = component.get('ConsoleUI') |
| 75 | if options['set']: |
| 76 | return self._set_option(*args, **options) |
| 77 | else: |
| 78 | return self._get_option(*args, **options) |
| 79 | |
| 80 | |
| 81 | def _get_option(self, *args, **options): |
| 82 | |
| 83 | def on_torrents_status(status): |
| 84 | for torrentid, data in status.items(): |
| 85 | self.console.write('\n') |
| 86 | if 'name' in data: |
| 87 | self.console.write('{!info!}Name: {!input!}%s' % data.get('name')) |
| 88 | self.console.write('{!info!}ID: {!input!}%s' % torrentid) |
| 89 | for k, v in data.items(): |
| 90 | if k != 'name': |
| 91 | self.console.write('{!info!}%s: {!input!}%s' % (k, v)) |
| 92 | |
| 93 | def on_torrents_status_fail(reason): |
| 94 | self.console.write('{!error!}Failed to get torrent data.') |
| 95 | |
| 96 | torrent_ids = [] |
| 97 | request_options = [] |
| 98 | |
| 99 | for arg in args: |
| 100 | if arg in torrent_options: |
| 101 | request_options.append(arg) |
| 102 | else: |
| 103 | ids = self.console.match_torrent(arg) |
| 104 | if not ids: |
| 105 | self.console.write("{!error!}The argument '" + arg + "' is not a recognized option nor did it match any torrents") |
| 106 | return |
| 107 | torrent_ids.extend(ids) |
| 108 | |
| 109 | if not torrent_ids: |
| 110 | torrent_ids.extend(self.console.match_torrent("")) |
| 111 | |
| 112 | if not request_options: |
| 113 | request_options = [ opt for opt in torrent_options.keys() ] |
| 114 | request_options.append('name') |
| 115 | |
| 116 | d = client.core.get_torrents_status({'id': torrent_ids}, request_options) |
| 117 | d.addCallback(on_torrents_status) |
| 118 | d.addErrback(on_torrents_status_fail) |
| 119 | return d |
| 120 | |
| 121 | |
| 122 | def _set_option(self, *args, **options): |
| 123 | deferred = defer.Deferred() |
| 124 | torrent_ids = [] |
| 125 | for arg in args: |
| 126 | ids = self.console.match_torrent(arg) |
| 127 | if not ids: |
| 128 | self.console.write("{!error!}The argument '" + arg + "' did not match any torrents") |
| 129 | return |
| 130 | torrent_ids.extend(ids) |
| 131 | if not torrent_ids: |
| 132 | self.console.write('{!error!}No torrents named') |
| 133 | return |
| 134 | key = options['set'][0] |
| 135 | val = options['set'][1] |
| 136 | |
| 137 | if key not in torrent_options: |
| 138 | self.console.write("{!error!}The key '%s' is invalid!" % key) |
| 139 | return |
| 140 | |
| 141 | val = torrent_options[key](val) |
| 142 | |
| 143 | def on_set_config(result): |
| 144 | self.console.write('{!success!}Torrent option successfully updated.') |
| 145 | deferred.callback(True) |
| 146 | |
| 147 | self.console.write('Setting %s to %s for torrents %s...' % (key, val, torrent_ids)) |
| 148 | client.core.set_torrent_options(torrent_ids, {key: val}).addCallback(on_set_config) |
| 149 | return deferred |
| 150 | |
| 151 | def complete(self, text): |
| 152 | torrents = component.get('ConsoleUI').tab_complete_torrent(text) |
| 153 | options = [ x for x in torrent_options if x.startswith(text) ] |
| 154 | # This should probably only return options immediately after --set. |
| 155 | return torrents + options |