diff options
Diffstat (limited to 'transmission-remote-cli.py')
| -rwxr-xr-x | transmission-remote-cli.py | 213 | 
1 files changed, 134 insertions, 79 deletions
| diff --git a/transmission-remote-cli.py b/transmission-remote-cli.py index ff7a487..abcca78 100755 --- a/transmission-remote-cli.py +++ b/transmission-remote-cli.py @@ -16,10 +16,10 @@  # http://www.gnu.org/licenses/gpl-3.0.txt                              #  ######################################################################## -VERSION = '1.2.2' +VERSION = '1.3'  TRNSM_VERSION_MIN = '1.90' -TRNSM_VERSION_MAX = '2.51' +TRNSM_VERSION_MAX = '2.52'  RPC_VERSION_MIN = 8  RPC_VERSION_MAX = 14 @@ -148,8 +148,8 @@ class ColorManager:          bg_name = pair.split(',')[1].split(':')[1].upper()          fg_name = pair.split(',')[0].split(':')[1].upper()          return { 'id': len(self.config.keys()) + 1, -                 'bg':eval('curses.COLOR_' + bg_name), -                 'fg':eval('curses.COLOR_' + fg_name) } +                 'bg': eval('curses.COLOR_' + bg_name), +                 'fg': eval('curses.COLOR_' + fg_name) }      def get_id(self, name): return self.config[name]['id']      def get_bg(self, name): return self.config[name]['bg'] @@ -157,6 +157,25 @@ class ColorManager:      def get_names(self): return self.config.keys() +class Normalizer: +    def __init__(self): +        self.values = {} + +    def add(self, id, value, max): +        if not id in self.values.keys(): +            self.values[id] = [ float(value) ] +        else: +            if len(self.values[id]) >= max: +                self.values[id].pop(0) +            self.values[id].append(float(value)) +        return self.get(id) + +    def get(self, id): +        if not id in self.values.keys(): +            return 0.0 +        return sum(self.values[id]) / len(self.values[id]) + +  authhandler = None  session_id = 0 @@ -1734,8 +1753,8 @@ class Interface:              copies_distributed = (float(t['uploadedEver']) / float(t['sizeWhenDone']))          except ZeroDivisionError:              copies_distributed = 0 -        info.append(['Upload: ', "%s " % scale_bytes(t['uploadedEver'], 'long') + \ -                         "(%.2f copies) distributed;  " % copies_distributed]) +        info.append(['Upload: ', "%s (%d%%) transmitted; " % +                     (scale_bytes(t['uploadedEver'], 'long'), t['uploadRatio']*100)])          if t['rateUpload']:              info[-1].append("sending %s per second" % scale_bytes(t['rateUpload'], 'long'))              if t['uploadLimited']: @@ -1743,6 +1762,20 @@ class Interface:          else:              info[-1].append("no transmission in progress") +        info.append(['Ratio: ', '%.2f copies distributed' % copies_distributed]) +        norm_upload_rate = norm.add('%s:rateUpload' % t['id'], t['rateUpload'], 15) +        if norm_upload_rate > 0: +            target_ratio = self.get_target_ratio() +            bytes_left   = (max(t['downloadedEver'],t['sizeWhenDone']) * target_ratio) - t['uploadedEver'] +            time_left    = bytes_left / norm_upload_rate +            info[-1][-1] += ';  ' +            if time_left < 86400:   # 1 day +                info[-1].append('approaching %.2f at %s' % \ +                                    (target_ratio, timestamp(time.time() + time_left, "%R"))) +            else: +                info[-1].append('approaching %.2f on %s' % \ +                                    (target_ratio, timestamp(time.time() + time_left, "%x"))) +          info.append(['Seed limit: '])          if t['seedRatioMode'] == 0:              if self.stats['seedRatioLimited']: @@ -1790,6 +1823,22 @@ class Interface:          self.draw_details_eventdates(ypos+1)          return ypos+1 +    def get_target_ratio(self): +        t = self.torrent_details +        if t['seedRatioMode'] == 1: +            return t['seedRatioLimit']              # individual limit +        elif t['seedRatioMode'] == 0 and self.stats['seedRatioLimited']: +            return self.stats['seedRatioLimit']     # global limit +        else: +            # round up to next 10/5/1 +            if t['uploadRatio'] >= 100: +                step_size = 10.0 +            elif t['uploadRatio'] >= 10: +                step_size = 5.0 +            else: +                step_size = 1.0 +            return int(round((t['uploadRatio'] + step_size/2) / step_size) * step_size) +      def draw_details_eventdates(self, ypos):          t = self.torrent_details @@ -2810,12 +2859,11 @@ def scale_time(seconds, type='short'):              return "%dy" % years -def timestamp(timestamp): +def timestamp(timestamp, format="%x %X"):      if timestamp < 1:          return 'never' -    date_format = "%x %X" -    absolute = time.strftime(date_format, time.localtime(timestamp)) +    absolute = time.strftime(format, time.localtime(timestamp))      if timestamp > time.time():          relative = 'in ' + scale_time(int(timestamp - time.time()), 'long')      else: @@ -3061,76 +3109,83 @@ def parse_sort_str(sort_str):              sort_orders.append( { 'name':x[0], 'reverse':False } )      return sort_orders +def show_version(option, opt_str, value, parser): +    quit("transmission-remote-cli %s  (supports Transmission %s-%s)\n" % \ +         (VERSION, TRNSM_VERSION_MIN, TRNSM_VERSION_MAX)) + + +if __name__ == '__main__': +    # command line parameters +    default_config_path = os.environ['HOME'] + '/.config/transmission-remote-cli/settings.cfg' +    parser = OptionParser(usage="%prog [options] [-- transmission-remote options]", +                          description="%%prog %s" % VERSION) +    parser.add_option("-v", "--version", action="callback", callback=show_version, +                      help="Show version number and supported Transmission versions.") +    parser.add_option("-c", "--connect", action="store", dest="connection", default="", +                      help="Point to the server using pattern [username:password@]host[:port]/[path]") +    parser.add_option("-s", "--ssl", action="store_true", dest="ssl", default=False, +                      help="Connect to Transmission using SSL.") +    parser.add_option("-f", "--config", action="store", dest="configfile", default=default_config_path, +                      help="Path to configuration file.") +    parser.add_option("--create-config", action="callback", callback=create_config, +                      help="Create configuration file CONFIGFILE with default values.") +    parser.add_option("-n", "--netrc", action="store_true", dest="use_netrc", default=False, +                      help="Get authentication info from your ~/.netrc file.") +    parser.add_option("--debug", action="store_true", dest="DEBUG", default=False, +                      help="Everything passed to the debug() function will be added to the file debug.log.") +    (cmd_args, transmissionremote_args) = parser.parse_args() + + +    # read config from config file +    config.read(cmd_args.configfile) + +    # command line connection data can override config file +    if cmd_args.connection: +        host, port, path, username, password = explode_connection_string(cmd_args.connection) +        config.set('Connection', 'host', host) +        config.set('Connection', 'port', str(port)) +        config.set('Connection', 'path', path) +        config.set('Connection', 'username', username) +        config.set('Connection', 'password', password) +    if cmd_args.use_netrc: +        username, password = read_netrc(hostname=config.get('Connection','host')) +        config.set('Connection', 'username', username) +        config.set('Connection', 'password', password) +    if cmd_args.ssl: +        config.set('Connection', 'ssl', 'True') -# command line parameters -default_config_path = os.environ['HOME'] + '/.config/transmission-remote-cli/settings.cfg' -parser = OptionParser(usage="%prog [options] [-- transmission-remote options]", -                      version="%%prog %s" % VERSION, -                      description="%%prog %s" % VERSION) -parser.add_option("-c", "--connect", action="store", dest="connection", default="", -                  help="Point to the server using pattern [username:password@]host[:port]/[path]") -parser.add_option("-s", "--ssl", action="store_true", dest="ssl", default=False, -                  help="Connect to transmission using SSL.") -parser.add_option("-f", "--config", action="store", dest="configfile", default=default_config_path, -                  help="Path to configuration file.") -parser.add_option("--create-config", action="callback", callback=create_config, -                  help="Create configuration file CONFIGFILE with default values.") -parser.add_option("-n", "--netrc", action="store_true", dest="use_netrc", default=False, -                  help="Get authentication info from your ~/.netrc file.") -parser.add_option("--debug", action="store_true", dest="DEBUG", default=False, -                  help="Everything passed to the debug() function will be added to the file debug.log.") -(cmd_args, transmissionremote_args) = parser.parse_args() - - -# read config from config file -config.read(cmd_args.configfile) - -# command line connection data can override config file -if cmd_args.connection: -    host, port, path, username, password = explode_connection_string(cmd_args.connection) -    config.set('Connection', 'host', host) -    config.set('Connection', 'port', str(port)) -    config.set('Connection', 'path', path) -    config.set('Connection', 'username', username) -    config.set('Connection', 'password', password) -if cmd_args.use_netrc: -    username, password = read_netrc(hostname=config.get('Connection','host')) -    config.set('Connection', 'username', username) -    config.set('Connection', 'password', password) -if cmd_args.ssl: -    config.set('Connection', 'ssl', 'True') - - - -# forward arguments after '--' to transmission-remote -if transmissionremote_args: -    cmd = ['transmission-remote', '%s:%s' % -           (config.get('Connection', 'host'), config.get('Connection', 'port'))] - -    # one argument and it doesn't start with '-' --> treat it like it's a torrent link/url -    if len(transmissionremote_args) == 1 and not transmissionremote_args[0].startswith('-'): -        cmd.extend(['-a', transmissionremote_args[0]]) - -    if config.get('Connection', 'username') and config.get('Connection', 'password'): -        cmd_print = cmd -        cmd_print.extend(['--auth', '%s:PASSWORD' % config.get('Connection', 'username')]) -        print "EXECUTING:\n%s\nRESPONSE:" % ' '.join(cmd_print) -        cmd.extend(['--auth', '%s:%s' % (config.get('Connection', 'username'), config.get('Connection', 'password'))]) -        cmd.extend(transmissionremote_args) -    else: -        print "EXECUTING:\n%s\nRESPONSE:" % ' '.join(cmd) -    try: -        retcode = call(cmd) -    except OSError, msg: -        quit("Could not execute the above command: %s\n" % msg, 128) -    quit('', retcode) - - -server = Transmission(config.get('Connection', 'host'), -                      config.getint('Connection', 'port'), -                      config.get('Connection', 'path'), -                      config.get('Connection', 'username'), -                      config.get('Connection', 'password')) -Interface() + +    # forward arguments after '--' to transmission-remote +    if transmissionremote_args: +        cmd = ['transmission-remote', '%s:%s' % +               (config.get('Connection', 'host'), config.get('Connection', 'port'))] + +        # one argument and it doesn't start with '-' --> treat it like it's a torrent link/url +        if len(transmissionremote_args) == 1 and not transmissionremote_args[0].startswith('-'): +            cmd.extend(['-a', transmissionremote_args[0]]) + +        if config.get('Connection', 'username') and config.get('Connection', 'password'): +            cmd_print = cmd +            cmd_print.extend(['--auth', '%s:PASSWORD' % config.get('Connection', 'username')]) +            print "EXECUTING:\n%s\nRESPONSE:" % ' '.join(cmd_print) +            cmd.extend(['--auth', '%s:%s' % (config.get('Connection', 'username'), config.get('Connection', 'password'))]) +            cmd.extend(transmissionremote_args) +        else: +            print "EXECUTING:\n%s\nRESPONSE:" % ' '.join(cmd) + +        try: +            retcode = call(cmd) +        except OSError, msg: +            quit("Could not execute the above command: %s\n" % msg, 128) +        quit('', retcode) + + +    norm = Normalizer() +    server = Transmission(config.get('Connection', 'host'), +                          config.getint('Connection', 'port'), +                          config.get('Connection', 'path'), +                          config.get('Connection', 'username'), +                          config.get('Connection', 'password')) +    Interface() | 
