(file) Return to btlaunchmanycurses.py CVS log (file) (dir) Up to [Development] / shadowsclient

Diff for /shadowsclient/btlaunchmanycurses.py between version 1.1 and 1.2

version 1.1, 2003/09/23 22:31:07 version 1.2, 2003/09/23 22:37:27
Line 1 
Line 1 
 #!/usr/bin/env python #!/usr/bin/env python
  
 # Written by Michael Janssen (jamuraa at base0 dot net) # Written by Michael Janssen (jamuraa at base0 dot net)
 # heavily borrowed code from btlaunchmany.py written by Bram Cohen  # originally heavily borrowed code from btlaunchmany.py by Bram Cohen
 # and btdownloadcurses.py written by Henry 'Pi' James # and btdownloadcurses.py written by Henry 'Pi' James
 # fmttime and fmtsize mercilessly stolen from btdownloadcurses. 0% of them are mine.  # now not so much.
   # fmttime and fmtsize stolen from btdownloadcurses.
 # see LICENSE.txt for license information # see LICENSE.txt for license information
  
 from BitTorrent.download import download from BitTorrent.download import download
 from threading import Thread, Event  from threading import Thread, Event, RLock
 from os import listdir from os import listdir
 from os.path import abspath, join, exists from os.path import abspath, join, exists
 from sys import argv, version, stdout, exit from sys import argv, version, stdout, exit
Line 19 
Line 20 
  
 def fmttime(n): def fmttime(n):
     if n == -1:     if n == -1:
        return 'download not progressing (file not being uploaded by others?)'          return 'download not progressing (no seeds?)'
     if n == 0:     if n == 0:
        return 'download complete!'        return 'download complete!'
     n = int(n)     n = int(n)
Line 29 
Line 30 
        return 'n/a'        return 'n/a'
     return 'finishing in %d:%02d:%02d' % (h, m, s)     return 'finishing in %d:%02d:%02d' % (h, m, s)
  
   
 def fmtsize(n): def fmtsize(n):
     unit = [' B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']      unit = [' B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
     i = 0     i = 0
     if (n > 999):     if (n > 999):
         i = 1         i = 1
Line 48 
Line 48 
 def dummy(*args, **kwargs): def dummy(*args, **kwargs):
     pass     pass
  
 def winch_handler(signum, stackframe):  threads = {}
     global scrwin, mainwin, mainwinw, headerwin, totalwin, statuswin  
     global scrpan, mainpan, headerpan, totalpan, statuspan  
     # SIGWINCH. Remake the frames!  
     ## Curses Trickery  
     curses.endwin()  
     # delete scrwin somehow?  
     scrwin.refresh()  
     scrwin = curses.newwin(0, 0, 0, 0)  
     scrh, scrw = scrwin.getmaxyx()  
     scrpan = curses.panel.new_panel(scrwin)  
     ### Curses Setup  
     scrh, scrw = scrwin.getmaxyx()  
     scrpan = curses.panel.new_panel(scrwin)  
     mainwinh = scrh - 5  # - 2 (bars) - 1 (debugwin) - 1 (borderwin) - 1 (totalwin)  
     mainwinw = scrw - 4  # - 2 (bars) - 2 (spaces)  
     mainwiny = 2         # + 1 (bar) + 1 (titles)  
     mainwinx = 2         # + 1 (bar) + 1 (space)  
     # + 1 to all windows so we can write at mainwinw  
     mainwin = curses.newwin(mainwinh, mainwinw+1, mainwiny, mainwinx)  
     mainpan = curses.panel.new_panel(mainwin)  
   
     headerwin = curses.newwin(1, mainwinw+1, 1, mainwinx)  
     headerpan = curses.panel.new_panel(headerwin)  
   
     totalwin = curses.newwin(1, mainwinw+1, scrh-3, mainwinx)  
     totalpan = curses.panel.new_panel(totalwin)  
   
     statuswin = curses.newwin(1, mainwinw+1, scrh-2, mainwinx)  
     statuspan = curses.panel.new_panel(statuswin)  
     mainwin.scrollok(0)  
     headerwin.scrollok(0)  
     totalwin.scrollok(0)  
     statuswin.addstr(0, 0, 'window resize: %s x %s' % (scrw, scrh))  
     statuswin.scrollok(0)  
     prepare_display()  
   
 ext = '.torrent' ext = '.torrent'
 wininfo = {}  status = 'btlaunchmany starting..'
   filecheck = RLock()
  
 def runmany(d, params):  def dropdir_mainloop(d, params):
     threads = []  
     deadfiles = []     deadfiles = []
     try:      global threads, status
         while 1:         while 1:
             files = listdir(d)             files = listdir(d)
             # new files             # new files
             for file in files:             for file in files:
                 if file[-len(ext):] == ext:                 if file[-len(ext):] == ext:
                     if file not in [x.getName() for x in threads] + deadfiles:                  if file not in threads.keys() + deadfiles:
                         wininfo[file] = {'basex': 2 * len(threads), 'killflag': Event()}                      threads[file] = {'kill': Event(), 'try': 1}
                         statuswin.erase()                      status = 'New torrent: %s' % file
                         statuswin.addnstr(0, 0,'new torrent detected: %s' % file, mainwinw)                      threads[file]['thread'] = Thread(target = StatusUpdater(join(d, file), params, file).download, name = file)
                         threads.append(Thread(target = SingleCursesDisplayer(join(d, file), params, file).download, name = file))                      threads[file]['thread'].start()
                         threads[-1].start()          # files with multiple tries
             # gone files          for file, threadinfo in threads.items():
             for i in range(len(threads)):              if threadinfo.get('timeout') == 0:
                 try:                  # Zero seconds left, try and start the thing again.
                     threadname = threads[i].getName()                  threadinfo['try'] = threadinfo['try'] + 1
                 except IndexError:                  threadinfo['thread'] = Thread(target = StatusUpdater(join(d, file), params, file).download, name = file)
                     # raised when we delete a thread from earlier, so the last ones fall out of range                  threadinfo['thread'].start()
                     continue                  threadinfo['timeout'] = -1
                 if not threads[i].isAlive():              elif threadinfo.get('timeout') > 0:
                     # died without "permission"                  # Decrement our counter by 1
                     deadfiles.append(threadname)                  threadinfo['timeout'] = threadinfo['timeout'] - 1
                     statuswin.erase()              elif not threadinfo['thread'].isAlive():
                     statuswin.addnstr(0, 0,'torrent died: %s' % threadname, mainwinw)                  # died without permission
                   if threadinfo.get('try') == 6:
                       # Died on the sixth try? You're dead.
                       deadfiles.append(file)
                       status = '%s died 6 times, added to dead list' % file
                       del threads[file]
                   else:
                       del threadinfo['thread']
                       threadinfo['timeout'] = 10
               # dealing with files that dissapear
               if file not in files:
                   status = 'Gone torrent: %s' % file
                   threadinfo['kill'].set()
                   threadinfo['thread'].join()
                   del threads[file]
           for file in deadfiles:
               # if the file dissapears, remove it from our dead list
               if file not in files:
                   deadfiles.remove(file)
           sleep(1)
  
                     # rearrange remaining windows  def display_thread(displaykiller):
                     mainwin.addnstr(wininfo[threadname]['basex'], 0, ' ' * mainwinw, mainwinw)      interval = 0.1
                     mainwin.addnstr(wininfo[threadname]['basex']+1, 0, ' ' * mainwinw, mainwinw)      global threads, status
                     for _, win in wininfo.items():      while 1:
                         if win['basex'] > wininfo[threadname]['basex']:          # display file info
                             win['basex'] = win['basex'] - 2          if (displaykiller.isSet()):
                     del wininfo[threadname]              break
                     del threads[i]          mainwin.erase()
                 elif threadname not in files:          winpos = 0
                     wininfo[threadname]['killflag'].set()  
                     # rearrange remaining windows  
                     mainwin.addnstr(wininfo[threadname]['basex'], 0, ' ' * mainwinw, mainwinw)  
                     mainwin.addnstr(wininfo[threadname]['basex']+1, 0, ' ' * mainwinw, mainwinw)  
                     for _, win in wininfo.items():  
                         if win['basex'] > wininfo[threadname]['basex']:  
                             win['basex'] = win['basex'] - 2  
                     threads[i].join()  
                     del wininfo[threadname]  
                     del threads[i]  
             # update the totals  
             totalup = 0             totalup = 0
             totaldown = 0             totaldown = 0
             for info in wininfo.values():          for file, threadinfo in threads.items():
                 totalup += info.get('uprate', 0)              uprate = threadinfo.get('uprate', 0)
                 totaldown += info.get('downrate', 0)              downrate = threadinfo.get('downrate', 0)
             stringup = '%s/s' % fmtsize(totalup)              uptxt = '%s/s' % fmtsize(uprate)
             stringdown = '%s/s' % fmtsize(totaldown)              downtxt = '%s/s' % fmtsize(downrate)
               filesize = threadinfo.get('filesize', 'N/A')
             totalwin.addnstr(0, mainwinw-20, ' ' * 20, 20)              mainwin.addnstr(winpos, 0, threadinfo.get('savefile', file), mainwinw - 28, curses.A_BOLD)
             totalwin.addnstr(0, mainwinw-20 + (10 - len(stringdown)), stringdown, 10)              mainwin.addnstr(winpos, mainwinw - 28 + (8 - len(filesize)), filesize, 8)
             totalwin.addnstr(0, mainwinw-10 + (10 - len(stringup)), stringup, 10)              mainwin.addnstr(winpos, mainwinw - 20 + (10 - len(downtxt)), downtxt, 10)
               mainwin.addnstr(winpos, mainwinw - 10 + (10 - len(uptxt)), uptxt, 10)
             sleep(1)              winpos = winpos + 1
     except KeyboardInterrupt:              mainwin.addnstr(winpos, 0, '^--- ', 5)
         statuswin.erase()              if threadinfo.get('timeout', 0) > 0:
         statuswin.addnstr(0, 0,'^C caught.. cleaning up.. ', mainwinw)                  mainwin.addnstr(winpos, 6, 'Try %d: died, retrying in %d' % (threadinfo.get('try', 1), threadinfo.get('timeout')), mainwinw - 5)
         curses.panel.update_panels()              else:
         curses.doupdate()                  mainwin.addnstr(winpos, 6, threadinfo.get('status',''), mainwinw - 5)
         for thread in threads:              winpos = winpos + 1
             threadname = thread.getName()              totalup += uprate
             statuswin.erase()              totaldown += downrate
             statuswin.addnstr(0, 0,'killing torrent %s' % threadname, mainwinw)          # display statusline
             curses.panel.update_panels()  
             curses.doupdate()  
             wininfo[threadname]['killflag'].set()  
             thread.join()  
         statuswin.erase()         statuswin.erase()
         statuswin.addnstr(0, 0,'Bye Bye!', mainwinw)          statuswin.addnstr(0, 0, status, mainwinw)
           # display totals line
           totaluptxt = '%s/s' % fmtsize(totaldown)
           totaldowntxt = '%s/s' % fmtsize(totalup)
   
           totalwin.erase()
           totalwin.addnstr(0, mainwinw - 27, 'Totals:', 7);
           totalwin.addnstr(0, mainwinw - 20 + (10 - len(totaluptxt)), totaluptxt, 10)
           totalwin.addnstr(0, mainwinw - 10 + (10 - len(totaldowntxt)), totaldowntxt, 10)
         curses.panel.update_panels()         curses.panel.update_panels()
         curses.doupdate()         curses.doupdate()
           sleep(interval)
  
   class StatusUpdater:
 class SingleCursesDisplayer:  
     def __init__(self, file, params, name):     def __init__(self, file, params, name):
         self.file = file         self.file = file
         self.params = params         self.params = params
         self.status = 'starting...'          self.name = name
         self.doingdown = ''          self.myinfo = threads[name]
         self.doingup = ''  
         self.done = 0         self.done = 0
         self.downfile = ''          self.checking = 0
         self.localfile = ''          self.activity = 'starting up...'
         self.fileSize = ''  
         self.activity = ''  
         self.myname = name  
         self.basex = wininfo[self.myname]['basex']  
         self.display()         self.display()
           self.myinfo['errors'] = []
  
     def download(self):     def download(self):
         download(self.params + ['--responsefile', self.file], self.choose, self.display, self.finished, self.err, wininfo[self.myname]['killflag'], mainwinw)          download(self.params + ['--responsefile', self.file], self.choose, self.display, self.finished, self.err, self.myinfo['kill'], 80)
         statuswin.erase();          status = 'Torrent %s stopped' % self.file
         statuswin.addnstr(0, 0, '%s: torrent stopped' % self.localfile, mainwinw)  
         curses.panel.update_panels()  
         curses.doupdate()  
  
     def finished(self):     def finished(self):
         self.done = 1         self.done = 1
         self.doingdown = '--- KB/s'          self.myinfo['done'] = 1
         self.activity = 'download succeeded!'         self.activity = 'download succeeded!'
         self.display(fractionDone = 1)         self.display(fractionDone = 1)
  
     def err(self, msg):     def err(self, msg):
         self.status = msg          self.myinfo['errors'].append(msg)
         self.display()         self.display()
  
     def failed(self):     def failed(self):
Line 206 
Line 175 
         self.display()         self.display()
  
     def choose(self, default, size, saveas, dir):     def choose(self, default, size, saveas, dir):
         self.downfile = default          global filecheck
         self.fileSize = fmtsize(size)          self.myinfo['downfile'] = default
           self.myinfo['filesize'] = fmtsize(size)
         if saveas == '':         if saveas == '':
             saveas = default             saveas = default
         self.localfile = abspath(saveas)          # it asks me where I want to save it before checking the file..
           if (exists(saveas)):
               # file will get checked
               while (not filecheck.acquire(blocking = 0) and not self.myinfo['kill'].isSet()):
                   self.myinfo['status'] = 'Waiting for disk check...'
                   sleep(0.1)
               self.checking = 1
           self.myinfo['savefile'] = saveas
         return saveas         return saveas
  
     def display(self, fractionDone = None, timeEst = None, downRate = None, upRate = None, activity = None):      def display(self, fractionDone = None, timeEst = None, downRate = None, upRate = None, activity = None, statistics = None, **kws):
         if self.basex != wininfo[self.myname]['basex']:          global filecheck, status
             # leave nothing but blank space  
             mainwin.addnstr(self.basex, 0, ' ' * 1000, mainwinw)  
             mainwin.addnstr(self.basex+1, 0, ' ' * 1000, mainwinw)  
             self.basex = wininfo[self.myname]['basex']  
         if activity is not None and not self.done:         if activity is not None and not self.done:
             self.activity = activity             self.activity = activity
         elif timeEst is not None:         elif timeEst is not None:
             self.activity = fmttime(timeEst)             self.activity = fmttime(timeEst)
         if fractionDone is not None:         if fractionDone is not None:
             self.status = '%s (%.1f%%)' % (self.activity, fractionDone * 100)              self.myinfo['status'] = '%s (%.1f%%)' % (self.activity, fractionDone * 100)
               if fractionDone == 1 and self.checking:
                   # we finished checking our files.
                   filecheck.release()
                   self.checking = 0
         else:         else:
             self.status = self.activity              self.myinfo['status'] = self.activity
         if downRate is None:         if downRate is None:
             downRate = 0             downRate = 0
         if upRate is None:         if upRate is None:
             upRate = 0             upRate = 0
         wininfo[self.myname]['downrate'] = int(downRate)          self.myinfo['uprate'] = int(upRate)
         wininfo[self.myname]['uprate'] = int(upRate)          self.myinfo['downrate'] = int(downRate)
         self.doingdown = '%s/s' % fmtsize(int(downRate))  
         self.doingup = '%s/s' % fmtsize(int(upRate))  
   
         # clear the stats section  
         mainwin.addnstr(self.basex, 0, ' ' * mainwinw, mainwinw)  
         mainwin.addnstr(self.basex, 0, self.downfile, mainwinw - 28, curses.A_BOLD)  
         mainwin.addnstr(self.basex, mainwinw - 28 + (8 - len(self.fileSize)), self.fileSize, 8)  
         mainwin.addnstr(self.basex, mainwinw - 20 + (10 - len(self.doingdown)), self.doingdown, 10)  
         mainwin.addnstr(self.basex, mainwinw - 10 + (10 - len(self.doingup)), self.doingup, 10)  
         # clear the status bar first  
         mainwin.addnstr(self.basex+1, 0, ' ' * mainwinw, mainwinw)  
         mainwin.addnstr(self.basex+1, 0, '^--- ', 5)  
         mainwin.addnstr(self.basex+1, 6, self.status, (mainwinw-1) - 5)  
         curses.panel.update_panels()  
         curses.doupdate()  
  
 def prepare_display(): def prepare_display():
     global mainwinw, scrwin, headerwin, totalwin     global mainwinw, scrwin, headerwin, totalwin
Line 256 
Line 218 
     headerwin.addnstr(0, mainwinw - 24, 'Size', 4);     headerwin.addnstr(0, mainwinw - 24, 'Size', 4);
     headerwin.addnstr(0, mainwinw - 18, 'Download', 8);     headerwin.addnstr(0, mainwinw - 18, 'Download', 8);
     headerwin.addnstr(0, mainwinw -  6, 'Upload', 6);     headerwin.addnstr(0, mainwinw -  6, 'Upload', 6);
   
     totalwin.addnstr(0, mainwinw - 27, 'Totals:', 7);     totalwin.addnstr(0, mainwinw - 27, 'Totals:', 7);
   
     curses.panel.update_panels()     curses.panel.update_panels()
     curses.doupdate()     curses.doupdate()
  
   def winch_handler(signum, stackframe):
       global scrwin, mainwin, mainwinw, headerwin, totalwin, statuswin
       global scrpan, mainpan, headerpan, totalpan, statuspan
       # SIGWINCH. Remake the frames!
       ## Curses Trickery
       curses.endwin()
       # delete scrwin somehow?
       scrwin.refresh()
       scrwin = curses.newwin(0, 0, 0, 0)
       scrh, scrw = scrwin.getmaxyx()
       scrpan = curses.panel.new_panel(scrwin)
       ### Curses Setup
       scrh, scrw = scrwin.getmaxyx()
       scrpan = curses.panel.new_panel(scrwin)
       mainwinh = scrh - 5  # - 2 (bars) - 1 (debugwin) - 1 (borderwin) - 1 (totalwin)
       mainwinw = scrw - 4  # - 2 (bars) - 2 (spaces)
       mainwiny = 2         # + 1 (bar) + 1 (titles)
       mainwinx = 2         # + 1 (bar) + 1 (space)
       # + 1 to all windows so we can write at mainwinw
       mainwin = curses.newwin(mainwinh, mainwinw+1, mainwiny, mainwinx)
       mainpan = curses.panel.new_panel(mainwin)
   
       headerwin = curses.newwin(1, mainwinw+1, 1, mainwinx)
       headerpan = curses.panel.new_panel(headerwin)
   
       totalwin = curses.newwin(1, mainwinw+1, scrh-3, mainwinx)
       totalpan = curses.panel.new_panel(totalwin)
   
       statuswin = curses.newwin(1, mainwinw+1, scrh-2, mainwinx)
       statuspan = curses.panel.new_panel(statuswin)
       mainwin.scrollok(0)
       headerwin.scrollok(0)
       totalwin.scrollok(0)
       statuswin.addstr(0, 0, 'window resize: %s x %s' % (scrw, scrh))
       statuswin.scrollok(0)
       prepare_display()
   
 if __name__ == '__main__': if __name__ == '__main__':
     if (len(argv) < 2):     if (len(argv) < 2):
         print """Usage: btlaunchmanycurses.py <directory> <global options>         print """Usage: btlaunchmanycurses.py <directory> <global options>
Line 272 
Line 269 
     try:     try:
         import curses         import curses
         import curses.panel         import curses.panel
   
         scrwin = curses.initscr()         scrwin = curses.initscr()
         curses.noecho()         curses.noecho()
         curses.cbreak()         curses.cbreak()
Line 280 
Line 276 
         print 'Textmode GUI initialization failed, cannot proceed.'         print 'Textmode GUI initialization failed, cannot proceed.'
         exit(-1)         exit(-1)
     try:     try:
         try:  
             signal(SIGWINCH, winch_handler)             signal(SIGWINCH, winch_handler)
             ### Curses Setup             ### Curses Setup
             scrh, scrw = scrwin.getmaxyx()             scrh, scrw = scrwin.getmaxyx()
Line 307 
Line 302 
             statuswin.addstr(0, 0, 'btlaunchmany started')             statuswin.addstr(0, 0, 'btlaunchmany started')
             statuswin.scrollok(0)             statuswin.scrollok(0)
             prepare_display()             prepare_display()
             curses.panel.update_panels()          displaykiller = Event()
             curses.doupdate()          displaythread = Thread(target = display_thread, name = 'display', args = [displaykiller])
             runmany(argv[1], argv[2:])          displaythread.setDaemon(1)
         finally:          displaythread.start()
           dropdir_mainloop(argv[1], argv[2:])
       except KeyboardInterrupt:
           status = '^C caught! Killing torrents..'
           for file, threadinfo in threads.items():
               status = 'Killing torrent %s' % file
               threadinfo['kill'].set()
               threadinfo['thread'].join()
               del threads[file]
           displaykiller.set()
           displaythread.join()
             curses.nocbreak()             curses.nocbreak()
             curses.echo()             curses.echo()
             curses.endwin()             curses.endwin()
     except:     except:
           curses.nocbreak()
           curses.echo()
           curses.endwin()
         traceback.print_exc()         traceback.print_exc()


Legend:
Removed from v.1.1  
changed lines
  Added in v.1.2

No CVS admin address has been configured
Powered by
ViewCVS 0.9.3