📄 btlaunchmanycurses.py
字号:
#!/usr/bin/env python# The contents of this file are subject to the BitTorrent Open Source License# Version 1.0 (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 Hoffmanfrom __future__ import divisionDOWNLOAD_SCROLL_RATE = 1import sys, osfrom threading import Eventfrom time import time, localtime, strftimefrom BitTorrent.launchmanycore import LaunchManyfrom BitTorrent.defaultargs import get_defaultsfrom BitTorrent.parseargs import parseargs, printHelpfrom BitTorrent import configfilefrom BitTorrent import versionfrom BitTorrent import BTFailuretry: import curses import curses.panel from curses.wrapper import wrapper as curses_wrapper from signal import signal, SIGWINCHexcept: 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 sizedef ljust(s, size): s = s[:size] return s + (' '*(size-len(s)))def rjust(s, size): s = s[:size] return (' '*(size-len(s)))+sclass 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() 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 self.headerwin.addnstr(0, 2, '#', self.mainwinw - 25, curses.A_BOLD) self.headerwin.addnstr(0, 4, 'Filename', self.mainwinw - 25, curses.A_BOLD) self.headerwin.addnstr(0, self.mainwinw - 24, 'Size', 4, curses.A_BOLD) self.headerwin.addnstr(0, self.mainwinw - 18, 'Download', 8, curses.A_BOLD) self.headerwin.addnstr(0, self.mainwinw - 6, 'Upload', 6, curses.A_BOLD) 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 up %s dn' % ( progress, status, peers, seeds, dist, fmtsize(upamt), fmtsize(dnamt) ) 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), 'btlaunchmanycurses')if __name__ == '__main__': uiname = 'btlaunchmanycurses' 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]
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -