From 410154779451b21ea14d7f6272c320059a772949 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Tue, 12 Aug 2008 22:00:45 -0500 Subject: Import files from BitTorrent 4.4.0 http://download.bittorrent.com/dl/BitTorrent-4.4.0.tar.gz --- launchmany-curses.py | 302 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100755 launchmany-curses.py (limited to 'launchmany-curses.py') diff --git a/launchmany-curses.py b/launchmany-curses.py new file mode 100755 index 0000000..86f8329 --- /dev/null +++ b/launchmany-curses.py @@ -0,0 +1,302 @@ +#!/usr/bin/env python + +# The contents of this file are subject to the BitTorrent Open Source License +# Version 1.1 (the License). You may not copy or use this file, in either +# source code or executable form, except in compliance with the License. You +# may obtain a copy of the License at http://www.bittorrent.com/license/. +# +# Software distributed under the License is distributed on an AS IS basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. + +# Written by John Hoffman + +from __future__ import division + +from BitTorrent.platform import install_translation +install_translation() + +DOWNLOAD_SCROLL_RATE = 1 + +import sys, os +from threading import Event +from time import time, localtime, strftime + +from BitTorrent.obsoletepythonsupport import * +from BitTorrent.launchmanycore import LaunchMany +from BitTorrent.defaultargs import get_defaults +from BitTorrent.parseargs import parseargs, printHelp +from BitTorrent import configfile +from BitTorrent import version +from BitTorrent import BTFailure + +try: + curses = import_curses() + import curses.panel + from curses.wrapper import wrapper as curses_wrapper + from signal import signal, SIGWINCH +except: + print _("Textmode GUI initialization failed, cannot proceed.") + print + print _("This download interface requires the standard Python module " + "\"curses\", which is unfortunately not available for the native " + "Windows port of Python. It is however available for the Cygwin " + "port of Python, running on all Win32 systems (www.cygwin.com).") + print + print _("You may still use \"btdownloadheadless.py\" to download.") + sys.exit(1) + +exceptions = [] + +def fmttime(n): + if n <= 0: + return None + n = int(n) + m, s = divmod(n, 60) + h, m = divmod(m, 60) + if h > 1000000: + return _("connecting to peers") + return _("ETA in %d:%02d:%02d") % (h, m, s) + +def fmtsize(n): + n = long(n) + unit = [' B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'] + i = 0 + if (n > 999): + i = 1 + while i + 1 < len(unit) and (n >> 10) >= 999: + i += 1 + n >>= 10 + n /= 1024 + if i > 0: + size = '%.1f' % n + '%s' % unit[i] + else: + size = '%.0f' % n + '%s' % unit[i] + return size + +def ljust(s, size): + s = s[:size] + return s + (' '*(size-len(s))) + +def rjust(s, size): + s = s[:size] + return (' '*(size-len(s)))+s + + +class CursesDisplayer(object): + + def __init__(self, scrwin): + self.messages = [] + self.scroll_pos = 0 + self.scroll_time = 0 + + self.scrwin = scrwin + signal(SIGWINCH, self.winch_handler) + self.changeflag = Event() + self._remake_window() + curses.use_default_colors() + + def winch_handler(self, signum, stackframe): + self.changeflag.set() + curses.endwin() + self.scrwin.refresh() + self.scrwin = curses.newwin(0, 0, 0, 0) + self._remake_window() + self._display_messages() + + def _remake_window(self): + self.scrh, self.scrw = self.scrwin.getmaxyx() + self.scrpan = curses.panel.new_panel(self.scrwin) + self.mainwinh = (2*self.scrh)//3 + self.mainwinw = self.scrw - 4 # - 2 (bars) - 2 (spaces) + self.mainwiny = 2 # + 1 (bar) + 1 (titles) + self.mainwinx = 2 # + 1 (bar) + 1 (space) + # + 1 to all windows so we can write at mainwinw + + self.mainwin = curses.newwin(self.mainwinh, self.mainwinw+1, + self.mainwiny, self.mainwinx) + self.mainpan = curses.panel.new_panel(self.mainwin) + self.mainwin.scrollok(0) + self.mainwin.nodelay(1) + + self.headerwin = curses.newwin(1, self.mainwinw+1, + 1, self.mainwinx) + self.headerpan = curses.panel.new_panel(self.headerwin) + self.headerwin.scrollok(0) + + self.totalwin = curses.newwin(1, self.mainwinw+1, + self.mainwinh+1, self.mainwinx) + self.totalpan = curses.panel.new_panel(self.totalwin) + self.totalwin.scrollok(0) + + self.statuswinh = self.scrh-4-self.mainwinh + self.statuswin = curses.newwin(self.statuswinh, self.mainwinw+1, + self.mainwinh+3, self.mainwinx) + self.statuspan = curses.panel.new_panel(self.statuswin) + self.statuswin.scrollok(0) + + try: + self.scrwin.border(ord('|'),ord('|'),ord('-'),ord('-'),ord(' '),ord(' '),ord(' '),ord(' ')) + except: + pass + rcols = (_("Size"),_("Download"),_("Upload")) + rwids = (8, 10, 10) + rwid = sum(rwids) + start = self.mainwinw - rwid + self.headerwin.addnstr(0, 2, '#', start, curses.A_BOLD) + self.headerwin.addnstr(0, 4, _("Filename"), start, curses.A_BOLD) + + for s,w in zip(rcols, rwids): + st = start + max(w - len(s), 0) + self.headerwin.addnstr(0, st, s[:w], len(s[:w]), curses.A_BOLD) + start += w + + self.totalwin.addnstr(0, self.mainwinw - 27, _("Totals:"), 7, curses.A_BOLD) + + self._display_messages() + + curses.panel.update_panels() + curses.doupdate() + self.changeflag.clear() + + + def _display_line(self, s, bold = False): + if self.disp_end: + return True + line = self.disp_line + self.disp_line += 1 + if line < 0: + return False + if bold: + self.mainwin.addnstr(line, 0, s, self.mainwinw, curses.A_BOLD) + else: + self.mainwin.addnstr(line, 0, s, self.mainwinw) + if self.disp_line >= self.mainwinh: + self.disp_end = True + return self.disp_end + + def _display_data(self, data): + if 3*len(data) <= self.mainwinh: + self.scroll_pos = 0 + self.scrolling = False + elif self.scroll_time + DOWNLOAD_SCROLL_RATE < time(): + self.scroll_time = time() + self.scroll_pos += 1 + self.scrolling = True + if self.scroll_pos >= 3*len(data)+2: + self.scroll_pos = 0 + + i = self.scroll_pos//3 + self.disp_line = (3*i)-self.scroll_pos + self.disp_end = False + + while not self.disp_end: + ii = i % len(data) + if i and not ii: + if not self.scrolling: + break + self._display_line('') + if self._display_line(''): + break + ( name, status, progress, peers, seeds, seedsmsg, dist, + uprate, dnrate, upamt, dnamt, size, t, msg ) = data[ii] + t = fmttime(t) + if t: + status = t + name = ljust(name,self.mainwinw-32) + size = rjust(fmtsize(size),8) + uprate = rjust('%s/s' % fmtsize(uprate),10) + dnrate = rjust('%s/s' % fmtsize(dnrate),10) + line = "%3d %s%s%s%s" % (ii+1, name, size, dnrate, uprate) + self._display_line(line, True) + if peers + seeds: + datastr = _(" (%s) %s - %s peers %s seeds %s dist copies - %s dn %s up") % ( + progress, status, peers, seeds, dist, + fmtsize(dnamt), fmtsize(upamt) ) + else: + datastr = ' '+status+' ('+progress+')' + self._display_line(datastr) + self._display_line(' '+ljust(msg,self.mainwinw-4)) + i += 1 + + def display(self, data): + if self.changeflag.isSet(): + return + + inchar = self.mainwin.getch() + if inchar == 12: # ^L + self._remake_window() + + self.mainwin.erase() + if data: + self._display_data(data) + else: + self.mainwin.addnstr( 1, self.mainwinw//2-5, + _("no torrents"), 12, curses.A_BOLD ) + totalup = 0 + totaldn = 0 + for ( name, status, progress, peers, seeds, seedsmsg, dist, + uprate, dnrate, upamt, dnamt, size, t, msg ) in data: + totalup += uprate + totaldn += dnrate + + totalup = '%s/s' % fmtsize(totalup) + totaldn = '%s/s' % fmtsize(totaldn) + + self.totalwin.erase() + self.totalwin.addnstr(0, self.mainwinw-27, _("Totals:"), 7, curses.A_BOLD) + self.totalwin.addnstr(0, self.mainwinw-20 + (10-len(totaldn)), + totaldn, 10, curses.A_BOLD) + self.totalwin.addnstr(0, self.mainwinw-10 + (10-len(totalup)), + totalup, 10, curses.A_BOLD) + + curses.panel.update_panels() + curses.doupdate() + + return inchar in (ord('q'),ord('Q')) + + def message(self, s): + self.messages.append(strftime('%x %X - ',localtime(time()))+s) + self._display_messages() + + def _display_messages(self): + self.statuswin.erase() + winpos = 0 + for s in self.messages[-self.statuswinh:]: + self.statuswin.addnstr(winpos, 0, s, self.mainwinw) + winpos += 1 + curses.panel.update_panels() + curses.doupdate() + + def exception(self, s): + exceptions.append(s) + self.message(_("SYSTEM ERROR - EXCEPTION GENERATED")) + + + +def LaunchManyWrapper(scrwin, config): + LaunchMany(config, CursesDisplayer(scrwin), 'launchmany-curses') + + +if __name__ == '__main__': + uiname = 'launchmany-curses' + defaults = get_defaults(uiname) + try: + if len(sys.argv) < 2: + printHelp(uiname, defaults) + sys.exit(1) + config, args = configfile.parse_configuration_and_args(defaults, + uiname, sys.argv[1:], 0, 1) + if args: + config['torrent_dir'] = args[0] + if not os.path.isdir(config['torrent_dir']): + raise BTFailure(_("Warning: ")+args[0]+_(" is not a directory")) + except BTFailure, e: + print _("error: ") + str(e) + _("\nrun with no args for parameter explanations") + sys.exit(1) + + curses_wrapper(LaunchManyWrapper, config) + if exceptions: + print _("\nEXCEPTION:") + print exceptions[0] -- cgit