From 61d5dfe4359a77db8837cf7ea643b44cc22d36ab Mon Sep 17 00:00:00 2001 From: Jonathan McCrohan Date: Thu, 7 Nov 2013 22:59:32 +0000 Subject: Imported Upstream version 1.6.3 --- NEWS | 9 +++ transmission-remote-cli | 154 +++++++++++++++++++++++++++++------------------- 2 files changed, 103 insertions(+), 60 deletions(-) diff --git a/NEWS b/NEWS index 9f5f440..5f62c47 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,12 @@ +1.6.3 2013-11-07 + BUGFIXES: + - Properly handle East-Asian wide characters in progress bar/torrent title + - New keybinding 'N': Start queued torrent + - Tab completion in torrent search dialog + - Faster (un)pausing of all torrents + - Display Transmission version in top bar + + 1.6.2 2013-08-09 - Support for Transmission version 2.82 diff --git a/transmission-remote-cli b/transmission-remote-cli index cba4d22..540a058 100755 --- a/transmission-remote-cli +++ b/transmission-remote-cli @@ -16,7 +16,7 @@ # http://www.gnu.org/licenses/gpl-3.0.txt # ######################################################################## -VERSION = '1.6.2' +VERSION = '1.6.3' TRNSM_VERSION_MIN = '1.90' TRNSM_VERSION_MAX = '2.82' @@ -294,7 +294,7 @@ class Transmission: DETAIL_FIELDS = [ 'files', 'priorities', 'wanted', 'peers', 'trackers', 'activityDate', 'dateCreated', 'startDate', 'doneDate', - 'totalSize', 'leftUntilDone', 'comment', + 'totalSize', 'leftUntilDone', 'comment', 'creator', 'hashString', 'pieceCount', 'pieceSize', 'pieces', 'downloadedEver', 'corruptEver', 'peersFrom' ] + LIST_FIELDS @@ -317,6 +317,7 @@ class Transmission: response = request.get_response() self.rpc_version = response['arguments']['rpc-version'] + self.version = response['arguments']['version'].split()[0] # rpc version too old? version_error = "Unsupported Transmission version: " + str(response['arguments']['version']) + \ @@ -347,7 +348,7 @@ class Transmission: if self.rpc_version >= 14: self.LIST_FIELDS.append('queuePosition'); self.DETAIL_FIELDS.append('queuePosition'); - + # set up request list self.requests = {'torrent-list': TransmissionRequest(host, port, path, 'torrent-get', self.TAG_TORRENT_LIST, {'fields': self.LIST_FIELDS}), @@ -653,13 +654,18 @@ class Transmission: else: return '' - def stop_torrent(self, id): - request = TransmissionRequest(self.host, self.port, self.path, 'torrent-stop', 1, {'ids': [id]}) + def stop_torrents(self, ids): + request = TransmissionRequest(self.host, self.port, self.path, 'torrent-stop', 1, {'ids': ids}) request.send_request() self.wait_for_torrentlist_update() - def start_torrent(self, id): - request = TransmissionRequest(self.host, self.port, self.path, 'torrent-start', 1, {'ids': [id]}) + def start_torrents(self, ids): + request = TransmissionRequest(self.host, self.port, self.path, 'torrent-start', 1, {'ids': ids}) + request.send_request() + self.wait_for_torrentlist_update() + + def start_now_torrent(self, id): + request = TransmissionRequest(self.host, self.port, self.path, 'torrent-start-now', 1, {'ids': [id]}) request.send_request() self.wait_for_torrentlist_update() @@ -849,7 +855,6 @@ class Interface: self.stats = server.get_global_stats() self.torrent_details = [] self.selected_torrent = -1 # changes to >-1 when focus >-1 & user hits return - self.all_paused = False self.highlight_dialog = False self.search_focus = 0 # like self.focus but for searches in torrent list self.focused_id = -1 # the id (provided by Transmission) of self.torrents[self.focus] @@ -892,6 +897,7 @@ class Interface: ord('K'): self.K_key, ord('p'): self.pause_unpause_torrent, ord('P'): self.pause_unpause_all_torrent, + ord('N'): self.start_now_torrent, ord('v'): self.verify_torrent, ord('y'): self.verify_torrent, ord('r'): self.r_key, @@ -1060,13 +1066,18 @@ class Interface: while True: server.update(1) - # display torrentlist - if self.selected_torrent == -1: - self.draw_torrent_list() - - # display some torrent's details - else: - self.draw_details() + # I'm catching all exceptions here because resizing the terminal + # can make a huge mess, e.g. we might be drawing to areas that + # don't exist anymore. The proper way would probably be to write a + # wrapper around self.pad.addstr() and anything else that uses + # coordinates, but it's not worth the effort as this part is + # called continuously. + try: + if self.selected_torrent == -1: + self.draw_torrent_list() + else: + self.draw_details() + except: pass self.stats = server.get_global_stats() self.draw_title_bar() # show shortcuts and stuff @@ -1197,7 +1208,7 @@ class Interface: self.file_pritority_or_switch_details(c) def add_torrent(self): - location = self.dialog_input_text("Add torrent from file or URL", homedir2tilde(os.getcwd()+os.sep), tab_complete='all') + location = self.dialog_input_text("Add torrent from file or URL", homedir2tilde(os.getcwd()+os.sep), tab_complete='files') if location: error = server.add_torrent(tilde2homedir(location)) if error: @@ -1320,19 +1331,24 @@ class Interface: else: t = self.torrents[self.focus] if t['status'] == Transmission.STATUS_STOPPED: - server.start_torrent(t['id']) + server.start_torrents([t['id']]) + else: + server.stop_torrents([t['id']]) + + def start_now_torrent(self, c): + if self.focus > -1: + if self.selected_torrent > -1: + t = self.torrent_details else: - server.stop_torrent(t['id']) + t = self.torrents[self.focus] + server.start_now_torrent(t['id']) def pause_unpause_all_torrent(self, c): - if self.all_paused: - for t in self.torrents: - server.start_torrent(t['id']) - self.all_paused = False + focused_torrent = self.torrents[ max(0,self.focus) ] + if focused_torrent['status'] == Transmission.STATUS_STOPPED: + server.start_torrents([t['id'] for t in self.torrents]) else: - for t in self.torrents: - server.stop_torrent(t['id']) - self.all_paused = True + server.stop_torrents([t['id'] for t in self.torrents]) def verify_torrent(self, c): if self.focus > -1: @@ -1644,16 +1660,15 @@ class Interface: c = self.screen.getch() if c == -1: return 0 - f = self.keybindings.get(c, None) if f: f(c) - - # update view - if self.selected_torrent == -1: - self.draw_torrent_list() - else: - self.draw_details() + try: + if self.selected_torrent == -1: + self.draw_torrent_list() + else: + self.draw_details() + except: pass def filter_torrent_list(self): unfiltered = self.torrents @@ -1834,13 +1849,19 @@ class Interface: tag_done += curses.A_BOLD if self.torrentname_is_progressbar: - # addstr() dies when you tell it to draw on the last column of the - # terminal, so we have to catch this exception. - try: - self.pad.addstr(ypos, 0, self.enc(title[0:bar_width]), tag_done) - self.pad.addstr(ypos, len_columns(title[0:bar_width]), self.enc(title[bar_width:]), tag) - except: - pass + # Estimate widths, which works for anything ASCII + bar_complete = title[:bar_width] + bar_incomplete = title[bar_width:] + # Adjust for East-Asian (wide) characters + while len_columns(bar_complete) != bar_width: + if len_columns(bar_complete) > bar_width: + bar_incomplete = bar_complete[-1] + bar_incomplete + bar_complete = bar_complete[:-1] + else: + bar_complete += bar_incomplete[0] + bar_incomplete = bar_incomplete[1:] + self.pad.addstr(ypos, 0, self.enc(bar_complete), tag_done) + self.pad.addstr(ypos, len_columns(bar_complete), self.enc(bar_incomplete), tag) else: self.pad.addstr(ypos, 0, self.enc(title), tag_done) @@ -2044,6 +2065,9 @@ class Interface: info.append(['Location: ',"%s" % homedir2tilde(t['downloadDir'])]) + if t['creator']: + info.append(['Creator: ',"%s" % t['creator']]) + ypos = self.draw_details_list(ypos, info) self.draw_details_eventdates(ypos+1) @@ -2579,7 +2603,7 @@ class Interface: self.draw_connection_status() self.draw_quick_help() def draw_connection_status(self): - status = "Transmission @ %s:%s" % (server.host, server.port) + status = "Transmission %s @ %s:%s" % (server.version, server.host, server.port) if cmd_args.DEBUG: status = "%d x %d " % (self.width, self.height) + status self.screen.addstr(0, 0, self.enc(status), curses.A_REVERSE) @@ -2618,6 +2642,7 @@ class Interface: " +/- Adjust bandwidth priority for focused torrent\n" + \ " p Pause/Unpause torrent\n" + \ " P Pause/Unpause all torrents\n" + \ + " N Start torrent now\n" + \ " v/y Verify torrent\n" + \ " m Move torrent\n" + \ " n Reannounce torrent\n" + \ @@ -2775,8 +2800,9 @@ class Interface: # tab_complete values: - # 'all': complete with any files/directories + # 'files': complete with any files/directories # 'dirs': complete only with directories + # 'torrent_list': complete with names from the torrent list # any false value: do not complete def dialog_input_text(self, message, input='', on_change=None, on_enter=None, tab_complete=None): width = self.width - 4 @@ -2838,29 +2864,37 @@ class Interface: index += 1 if on_change: on_change(input) elif c == ord('\t') and tab_complete: - (dirname, filename) = os.path.split(tilde2homedir(input)) - if not dirname: - dirname = unicode(os.getcwd()) - try: - possible_choices = [ choice for choice in os.listdir(dirname) - if choice.startswith(filename) ] - except OSError: - continue; - if tab_complete == 'dirs': - possible_choices = [ d for d in possible_choices - if os.path.isdir(os.path.join(dirname, d)) ] + possible_choices = []; + if tab_complete in ('files', 'dirs'): + (dirname, filename) = os.path.split(tilde2homedir(input)) + if not dirname: + dirname = unicode(os.getcwd()) + try: + possible_choices = [ os.path.join(dirname, choice) for choice in os.listdir(dirname) + if choice.startswith(filename) ] + except OSError: + continue; + if tab_complete == 'dirs': + possible_choices = [ d for d in possible_choices + if os.path.isdir(d) ] + elif tab_complete == 'torrent_list': + possible_choices = [ t['name'] for t in self.torrents + if t['name'].startswith(input) ] if(possible_choices): - input = os.path.join(dirname, os.path.commonprefix(possible_choices)) - if len(possible_choices) == 1 and os.path.isdir(input) and input.endswith(os.sep) == False: - input += os.sep - input = homedir2tilde(input) + input = os.path.commonprefix(possible_choices) + if tab_complete in ('files', 'dirs'): + if len(possible_choices) == 1 and os.path.isdir(input) and input.endswith(os.sep) == False: + input += os.sep + input = homedir2tilde(input) index = len(input) + if on_change: on_change(input); if on_change: win.redrawwin() def dialog_search_torrentlist(self, c): self.dialog_input_text('Search torrent by title:', on_change=self.draw_torrent_list, - on_enter=self.increment_search) + on_enter=self.increment_search, + tab_complete = 'torrent_list') def increment_search(self, input): self.search_focus += 1 @@ -3315,7 +3349,7 @@ def ljust_columns(text, max_width, padchar=' '): def len_columns(text): """ Returns the amount of columns that would occupy. """ - if type(text) == type(str()): + if isinstance(text, str): text = unicode(text, ENCODING) columns = 0 for character in text: @@ -3338,8 +3372,8 @@ def num2str(num, format='%s'): def debug(data): if cmd_args.DEBUG: file = open("debug.log", 'a') - if type(data) == type(str()): - file.write(data) + if isinstance(data, str) or isinstance(data, unicode): + file.write(data.encode('UTF-8', 'replace')) else: import pprint pp = pprint.PrettyPrinter(indent=4) -- cgit v1.2.3