home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2007 September / PCWSEP07.iso / Software / Linux / Linux Mint 3.0 Light / LinuxMint-3.0-Light.iso / casper / filesystem.squashfs / usr / bin / btdownloadcurses.bittorrent < prev    next >
Encoding:
Text File  |  2007-02-19  |  7.8 KB  |  230 lines

  1. #! /usr/bin/python
  2.  
  3. # Written by Henry 'Pi' James
  4. # see LICENSE.txt for license information
  5.  
  6. from BitTorrent.download import download
  7. from BitTorrent.fmt import fmtsize, fmttime
  8. from threading import Event
  9. from os.path import abspath
  10. from signal import signal, SIGWINCH
  11. from sys import argv, stdout
  12. from time import strftime, time
  13.  
  14. def commaize(n): 
  15.     s = str(n)
  16.     commad = s[-3:]
  17.     while len(s) > 3:
  18.         s = s[:-3]
  19.         commad = '%s,%s' % (s[-3:], commad)
  20.     return commad
  21.  
  22. def winch_handler(signum, stackframe):
  23.     global scrwin, scrpan, labelwin, labelpan, fieldw, fieldh, fieldwin, fieldpan
  24.     global winchanging
  25.     # SIGWINCH. Remake the frames!
  26.     winchanging = 1
  27.     ## Curses Trickery
  28.     curses.endwin()
  29.     # delete scrwin somehow?
  30.     scrwin.refresh()
  31.     scrwin = curses.newwin(0, 0, 0, 0) 
  32.     scrh, scrw = scrwin.getmaxyx()
  33.     scrpan = curses.panel.new_panel(scrwin)
  34.     labelh, labelw, labely, labelx = scrh - 2, 9, 1, 2
  35.     labelwin = curses.newwin(labelh, labelw, labely, labelx)
  36.     labelpan = curses.panel.new_panel(labelwin)
  37.     fieldh, fieldw, fieldy, fieldx = scrh - 2, scrw - 2 - labelw - 3, 1, labelw + 3
  38.     fieldwin = curses.newwin(fieldh, fieldw, fieldy, fieldx)
  39.     fieldpan = curses.panel.new_panel(fieldwin)
  40.     prepare_display()
  41.     winchanging = 0
  42.  
  43. # This flag stops the torrent when set.
  44. mainkillflag = Event()
  45. # This flag is set when winch is happening, so that we don't anger the curses gods
  46. # by writing off the side of the screen
  47. winchanging = 0
  48.  
  49. class CursesDisplayer:
  50.     def __init__(self, mainerrlist):
  51.         self.done = 0
  52.         self.file = ''
  53.         self.fileSize = ''
  54.         self.activity = ''
  55.         self.status = ''
  56.         self.progress = ''
  57.         self.downloadTo = ''
  58.         self.downRate = '%s/s down' % (fmtsize(0))
  59.         self.upRate = '%s/s up  ' % (fmtsize(0))
  60.         self.upTotal = '%s   up  ' % (fmtsize(0, 2))
  61.         self.downTotal = '%s   down' % (fmtsize(0, 2))
  62.         self.errors = []
  63.         self.globalerrlist = mainerrlist
  64.         self.last_update_time = 0
  65.  
  66.     def finished(self):
  67.         self.done = 1
  68.         self.activity = 'download succeeded!'
  69.         self.downRate = '%s/s down' % (fmtsize(0))
  70.         self.display({'fractionDone': 1})
  71.  
  72.     def failed(self):
  73.         global mainkillflag
  74.         if not mainkillflag.isSet():
  75.             self.done = 1
  76.             self.activity = 'download failed!'
  77.             self.downRate = '%s/s down' % (fmtsize(0))
  78.             self.display()
  79.  
  80.     def error(self, errormsg):
  81.         errtxt = strftime('[%H:%M:%S] ') + errormsg
  82.         self.errors.append(errtxt)
  83.         self.globalerrlist.append(errtxt)
  84.         # force redraw to get rid of nasty stack backtrace
  85.         winch_handler(SIGWINCH, 0)
  86.         self.display()
  87.  
  88.     def display(self, dict = {}):
  89.         if self.last_update_time + 0.1 > time() and dict.get('fractionDone') not in (0.0, 1.0) and not dict.has_key('activity'):
  90.             return
  91.         self.last_update_time = time()
  92.         global winchanging
  93.         if winchanging == 1: 
  94.           return
  95.         global mainkillflag
  96.         fractionDone = dict.get('fractionDone', None)
  97.         timeEst = dict.get('timeEst', None)
  98.         downRate = dict.get('downRate', None)
  99.         upRate = dict.get('upRate', None)
  100.         downTotal = dict.get('downTotal', None) # total download megs, float
  101.         upTotal = dict.get('upTotal', None) # total upload megs, float
  102.         activity = dict.get('activity', None)
  103.         if activity is not None and not self.done:
  104.             self.activity = activity
  105.         elif timeEst is not None:
  106.             self.activity = fmttime(timeEst)
  107.         if fractionDone is not None:
  108.             blocknum = int(fieldw * fractionDone)
  109.             self.progress = blocknum * '#' + (fieldw - blocknum) * '_'
  110.             self.status = '%s (%.1f%%)' % (self.activity, fractionDone * 100)
  111.         else:
  112.             self.status = self.activity
  113.         if downRate is not None:
  114.             self.downRate = '%s/s down' % (fmtsize(float(downRate)))
  115.         if upRate is not None:
  116.             self.upRate = '%s/s up  ' % (fmtsize(float(upRate)))
  117.         if upTotal is not None:
  118.             self.upTotal = '%s   up  ' % (fmtsize(upTotal, 2))
  119.         if downTotal is not None:
  120.             self.downTotal = '%s   down' % (fmtsize(downTotal, 2))
  121.         inchar = fieldwin.getch()
  122.         if inchar == 12: #^L
  123.             winch_handler(SIGWINCH, 0)
  124.         elif inchar == ord('q'):  # quit 
  125.             mainkillflag.set() 
  126.             self.status = 'shutting down...'
  127.         try:
  128.             fieldwin.erase()
  129.             fieldwin.addnstr(0, 0, self.file, fieldw, curses.A_BOLD)
  130.             fieldwin.addnstr(1, 0, self.fileSize, fieldw)
  131.             fieldwin.addnstr(2, 0, self.downloadTo, fieldw)
  132.             if self.progress:
  133.                 fieldwin.addnstr(3, 0, self.progress, fieldw, curses.A_BOLD)
  134.             fieldwin.addnstr(4, 0, self.status, fieldw)
  135.             fieldwin.addnstr(5, 0, self.downRate + ' - ' + self.upRate, fieldw / 2)
  136.             fieldwin.addnstr(6, 0, self.downTotal + ' - ' + self.upTotal, fieldw / 2)
  137.  
  138.             if self.errors:
  139.                 errsize = len(self.errors)
  140.                 for i in range(errsize):
  141.                     if (7 + i) >= fieldh:
  142.                         break
  143.                     fieldwin.addnstr(7 + i, 0, self.errors[errsize - i - 1], fieldw, curses.A_BOLD)
  144.             else:
  145.                 fieldwin.move(7, 0)
  146.         except curses.error: 
  147.             pass
  148.  
  149.         curses.panel.update_panels()
  150.         curses.doupdate()
  151.  
  152.     def chooseFile(self, default, size, saveas, dir):
  153.         self.file = default
  154.         self.fileSize = '%s (%s)' % (commaize(size), fmtsize(size, padded = 0))
  155.         if saveas == '':
  156.             saveas = default
  157.         self.downloadTo = abspath(saveas)
  158.         return saveas
  159.  
  160. def run(mainerrlist, params):
  161.     d = CursesDisplayer(mainerrlist)
  162.     try:
  163.         download(params, d.chooseFile, d.display, d.finished, d.error, mainkillflag, fieldw)
  164.     except KeyboardInterrupt:
  165.         # ^C to exit.. 
  166.         pass 
  167.     if not d.done:
  168.         d.failed()
  169.  
  170. def prepare_display():
  171.     try:
  172.         scrwin.border(ord('|'),ord('|'),ord('-'),ord('-'),ord(' '),ord(' '),ord(' '),ord(' '))
  173.         labelwin.addstr(0, 0, 'file:')
  174.         labelwin.addstr(1, 0, 'size:')
  175.         labelwin.addstr(2, 0, 'dest:')
  176.         labelwin.addstr(3, 0, 'progress:')
  177.         labelwin.addstr(4, 0, 'status:')
  178.         labelwin.addstr(5, 0, 'speed:')
  179.         labelwin.addstr(6, 0, 'totals:')
  180.         labelwin.addstr(7, 0, 'error(s):')
  181.     except curses.error: 
  182.         pass
  183.     fieldwin.nodelay(1)
  184.     curses.panel.update_panels()
  185.     curses.doupdate()
  186.  
  187. try:
  188.     import curses
  189.     import curses.panel
  190.  
  191.     scrwin = curses.initscr()
  192.     curses.noecho()
  193.     curses.cbreak()
  194.  
  195. except:
  196.     print 'Textmode GUI initialization failed, cannot proceed.'
  197.     print
  198.     print 'This download interface requires the standard Python module ' \
  199.        '"curses", which is unfortunately not available for the native ' \
  200.        'Windows port of Python. It is however available for the Cygwin ' \
  201.        'port of Python, running on all Win32 systems (www.cygwin.com).'
  202.     print
  203.     print 'You may still use "btdownloadheadless.py" to download.'
  204.  
  205. scrh, scrw = scrwin.getmaxyx()
  206. scrpan = curses.panel.new_panel(scrwin)
  207. labelh, labelw, labely, labelx = scrh - 2, 9, 1, 2
  208. labelwin = curses.newwin(labelh, labelw, labely, labelx)
  209. labelpan = curses.panel.new_panel(labelwin)
  210. fieldh, fieldw, fieldy, fieldx = scrh - 2, scrw - 2 - labelw - 3, 1, labelw + 3
  211. fieldwin = curses.newwin(fieldh, fieldw, fieldy, fieldx)
  212. fieldpan = curses.panel.new_panel(fieldwin)
  213. prepare_display()
  214.  
  215. signal(SIGWINCH, winch_handler)
  216.  
  217. if __name__ == '__main__':
  218.     mainerrlist = []
  219.     try:
  220.         run(mainerrlist, argv[1:])
  221.     finally:
  222.         curses.nocbreak()
  223.         curses.echo()
  224.         curses.endwin()
  225.     if len(mainerrlist) != 0:
  226.        print "These errors occurred during execution:"
  227.        for error in mainerrlist:
  228.           print error
  229.  
  230.