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

  1 theshadow 1.1 #!/usr/bin/env python
  2               
  3               # Written by Henry 'Pi' James
  4               # see LICENSE.txt for license information
  5               
  6 theshadow 1.7 SPEW_SCROLL_RATE = 3
  7               
  8 theshadow 1.3 import PSYCO
  9               if PSYCO.psyco:
 10                   try:
 11                       import psyco
 12                       assert psyco.__version__ >= 0x010100f0
 13                       psyco.full()
 14                   except:
 15                       pass
 16               
 17 theshadow 1.7 from BitTorrent.download import Download
 18 theshadow 1.1 from threading import Event
 19               from os.path import abspath
 20               from signal import signal, SIGWINCH
 21               from sys import argv, version, stdout
 22 theshadow 1.7 from time import time, strftime
 23 theshadow 1.1 assert version >= '2', "Install Python 2.0 or greater"
 24               
 25               def fmttime(n):
 26                   if n == -1:
 27                       return 'download not progressing (file not being uploaded by others?)'
 28                   if n == 0:
 29                       return 'download complete!'
 30                   n = int(n)
 31                   m, s = divmod(n, 60)
 32                   h, m = divmod(m, 60)
 33                   if h > 1000000:
 34                       return 'n/a'
 35                   return 'finishing in %d:%02d:%02d' % (h, m, s)
 36               
 37               def fmtsize(n):
 38                   s = str(n)
 39                   size = s[-3:]
 40                   while len(s) > 3:
 41                       s = s[:-3]
 42                       size = '%s,%s' % (s[-3:], size)
 43                   if n > 999:
 44 theshadow 1.1         unit = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
 45                       i = 1
 46                       while i + 1 < len(unit) and (n >> 10) >= 999:
 47                           i += 1
 48                           n >>= 10
 49                       n = float(n) / (1 << 10)
 50                       size = '%s (%.0f %s)' % (size, n, unit[i])
 51                   return size
 52               
 53               def winch_handler(signum, stackframe):
 54 theshadow 1.7     global scrwin, scrpan, labelwin, labelpan
 55                   global fieldh, fieldw, fieldy, fieldx, fieldwin, fieldpan
 56                   global spewwin, spewpan, spewh, speww, spewy, spewx
 57 theshadow 1.1     # SIGWINCH. Remake the frames!
 58                   ## Curses Trickery
 59 theshadow 1.7     try:
 60                       curses.endwin()
 61                   except:
 62                       pass
 63 theshadow 1.1     # delete scrwin somehow?
 64                   scrwin.refresh()
 65                   scrwin = curses.newwin(0, 0, 0, 0) 
 66                   scrh, scrw = scrwin.getmaxyx()
 67                   scrpan = curses.panel.new_panel(scrwin)
 68 theshadow 1.7     labelh, labelw, labely, labelx = 11, 9, 1, 2
 69 theshadow 1.1     labelwin = curses.newwin(labelh, labelw, labely, labelx)
 70                   labelpan = curses.panel.new_panel(labelwin)
 71 theshadow 1.7     fieldh, fieldw, fieldy, fieldx = labelh, scrw - 2 - labelw - 3, 1, labelw + 3
 72 theshadow 1.1     fieldwin = curses.newwin(fieldh, fieldw, fieldy, fieldx)
 73                   fieldpan = curses.panel.new_panel(fieldwin)
 74 theshadow 1.7     spewh, speww, spewy, spewx = scrh - labelh - 2, scrw - 3, 1 + labelh, 2
 75                   spewwin = curses.newwin(spewh, speww, spewy, spewx)
 76                   spewpan = curses.panel.new_panel(spewwin)
 77 theshadow 1.1     prepare_display()
 78               
 79               
 80               class CursesDisplayer:
 81                   def __init__(self, mainerrlist):
 82                       self.done = 0
 83                       self.file = ''
 84                       self.fileSize = ''
 85                       self.activity = ''
 86                       self.status = ''
 87                       self.progress = ''
 88                       self.downloadTo = ''
 89                       self.downRate = '---'
 90                       self.upRate = '---'
 91 theshadow 1.2         self.shareRating = ''
 92                       self.seedStatus = ''
 93                       self.peerStatus = ''
 94 theshadow 1.1         self.errors = []
 95                       self.globalerrlist = mainerrlist
 96 theshadow 1.6         self.last_update_time = 0
 97 theshadow 1.7         self.spew_scroll_time = 0
 98                       self.spew_scroll_pos = 0
 99 theshadow 1.1 
100                   def finished(self):
101                       self.done = 1
102                       self.activity = 'download succeeded!'
103                       self.downRate = '---'
104                       self.display(fractionDone = 1)
105               
106                   def failed(self):
107                       self.done = 1
108                       self.activity = 'download failed!'
109                       self.downRate = '---'
110                       self.display()
111               
112                   def error(self, errormsg):
113 theshadow 1.7         newerrmsg = strftime('[%H:%M:%S] ') + errormsg
114 theshadow 1.4         self.errors.append(newerrmsg)
115                       self.globalerrlist.append(newerrmsg)
116 theshadow 1.1         self.display()
117               
118                   def display(self, fractionDone = None, timeEst = None,
119 theshadow 1.2             downRate = None, upRate = None, activity = None,
120 theshadow 1.7             statistics = None, spew = None, **kws):
121 theshadow 1.6         if self.last_update_time + 0.1 > time() and fractionDone not in (0.0, 1.0) and activity is not None:
122                           return
123                       self.last_update_time = time()
124 theshadow 1.1         if activity is not None and not self.done:
125                           self.activity = activity
126                       elif timeEst is not None:
127                           self.activity = fmttime(timeEst)
128                       if fractionDone is not None:
129                           blocknum = int(fieldw * fractionDone)
130                           self.progress = blocknum * '#' + (fieldw - blocknum) * '_'
131                           self.status = '%s (%.1f%%)' % (self.activity, fractionDone * 100)
132                       else:
133                           self.status = self.activity
134                       if downRate is not None:
135                           self.downRate = '%.1f KB/s' % (float(downRate) / (1 << 10))
136                       if upRate is not None:
137                           self.upRate = '%.1f KB/s' % (float(upRate) / (1 << 10))
138 theshadow 1.2         if statistics is not None:
139                          if (statistics.shareRating < 0) or (statistics.shareRating > 100):
140                              self.shareRating = 'oo  (%.1f MB up / %.1f MB down)' % (float(statistics.upTotal) / (1<<20), float(statistics.downTotal) / (1<<20))
141                          else:
142                              self.shareRating = '%.3f  (%.1f MB up / %.1f MB down)' % (statistics.shareRating, float(statistics.upTotal) / (1<<20), float(statistics.downTotal) / (1<<20))
143                          if not self.done:
144                             self.seedStatus = '%d seen now, plus %.3f distributed copies' % (statistics.numSeeds,0.001*int(1000*statistics.numCopies))
145                          else:
146                             self.seedStatus = '%d seen recently, plus %.3f distributed copies' % (statistics.numOldSeeds,0.001*int(1000*statistics.numCopies))
147                          self.peerStatus = '%d seen now, %.1f%% done at %.1f kB/s' % (statistics.numPeers,statistics.percentDone,float(statistics.torrentRate) / (1 << 10))
148 theshadow 1.1 
149                       fieldwin.erase()
150                       fieldwin.addnstr(0, 0, self.file, fieldw, curses.A_BOLD)
151                       fieldwin.addnstr(1, 0, self.fileSize, fieldw)
152                       fieldwin.addnstr(2, 0, self.downloadTo, fieldw)
153                       if self.progress:
154                         fieldwin.addnstr(3, 0, self.progress, fieldw, curses.A_BOLD)
155                       fieldwin.addnstr(4, 0, self.status, fieldw)
156                       fieldwin.addnstr(5, 0, self.downRate, fieldw)
157                       fieldwin.addnstr(6, 0, self.upRate, fieldw)
158 theshadow 1.2         fieldwin.addnstr(7, 0, self.shareRating, fieldw)
159                       fieldwin.addnstr(8, 0, self.seedStatus, fieldw)
160                       fieldwin.addnstr(9, 0, self.peerStatus, fieldw)
161 theshadow 1.1 
162 theshadow 1.7         spewwin.erase()
163               
164                       if spew is None:
165                           errsize = spewh
166                           spewwin.addnstr(0, 0, "error(s):", speww, curses.A_BOLD)
167                           if self.errors:
168                               errsize = len(self.errors)
169                               displaysize = min(errsize, spewh)
170                               displaytop = errsize - displaysize
171                               for i in range(displaysize):
172                                   spewwin.addnstr(0, labelw, self.errors[displaytop + i],
173                                                speww-labelw-1, curses.A_BOLD)
174 theshadow 1.1         else:
175 theshadow 1.7             spewwin.addnstr(0, 0, "error:", speww, curses.A_BOLD)
176                           if self.errors:
177                               spewwin.addnstr(0, labelw, self.errors[-1],
178                                                speww-labelw-1, curses.A_BOLD)
179                           spewwin.addnstr(2, 0, " #      IP                 Upload           Download     Completed  Speed", speww, curses.A_BOLD)
180               
181               
182                           if self.spew_scroll_time + SPEW_SCROLL_RATE < time():
183                               self.spew_scroll_time = time()
184                               if len(spew) > spewh-5 or self.spew_scroll_pos > 0:
185                                   self.spew_scroll_pos += 1
186                           if self.spew_scroll_pos > len(spew)-1:
187                               self.spew_scroll_pos = 0
188               
189                           for i in range(len(spew)):
190                               spew[i]['lineno'] = i+1
191                           spew = spew[self.spew_scroll_pos:] + spew[:self.spew_scroll_pos]                
192                           
193                           for i in range(min(spewh - 5, len(spew))):
194                               spewwin.addnstr(i+3, 0, '%3d' % spew[i]['lineno'], 3)
195                               spewwin.addnstr(i+3, 4, spew[i]['ip'], 15)
196 theshadow 1.7                 if spew[i]['uprate'] > 100:
197                                   spewwin.addnstr(i+3, 20, '%6.0f KB/s' % (float(spew[i]['uprate']) / 1000), 11)
198                               spewwin.addnstr(i+3, 32, '-----', 5)
199                               if spew[i]['uinterested'] == 1:
200                                   spewwin.addnstr(i+3, 33, 'I', 1)
201                               if spew[i]['uchoked'] == 1:
202                                   spewwin.addnstr(i+3, 35, 'C', 1)
203                               if spew[i]['downrate'] > 100:
204                                   spewwin.addnstr(i+3, 38, '%6.0f KB/s' % (float(spew[i]['downrate']) / 1000), 11)
205                               spewwin.addnstr(i+3, 50, '-------', 7)
206                               if spew[i]['dinterested'] == 1:
207                                   spewwin.addnstr(i+3, 51, 'I', 1)
208                               if spew[i]['dchoked'] == 1:
209                                   spewwin.addnstr(i+3, 53, 'C', 1)
210                               if spew[i]['snubbed'] == 1:
211                                   spewwin.addnstr(i+3, 55, 'S', 1)
212                               spewwin.addnstr(i+3, 58, '%5.1f%%' % (float(int(spew[i]['completed']*1000))/10), 6)
213                               if spew[i]['speed'] is not None:
214                                   spewwin.addnstr(i+3, 64, '%5.0f KB/s' % (float(spew[i]['speed'])/1000), 10)
215               
216                           if statistics is not None:
217 theshadow 1.7                 spewwin.addnstr(spewh-1, 0,
218                                       'downloading %d pieces, have %d fragments, %d of %d pieces completed'
219                                       % ( statistics.storage_active, statistics.storage_dirty,
220                                           statistics.storage_numcomplete,
221                                           statistics.storage_totalpieces ), speww-1 )
222 theshadow 1.1 
223                       curses.panel.update_panels()
224                       curses.doupdate()
225               
226                   def chooseFile(self, default, size, saveas, dir):
227                       self.file = default
228                       self.fileSize = fmtsize(size)
229                       if saveas == '':
230                           saveas = default
231                       self.downloadTo = abspath(saveas)
232                       return saveas
233               
234               def run(mainerrlist, params):
235                   d = CursesDisplayer(mainerrlist)
236 theshadow 1.7     dow = Download()
237                   d.dow = dow
238 theshadow 1.1     try:
239 theshadow 1.7         dow.download(params, d.chooseFile, d.display, d.finished, d.error, Event(), fieldw)
240 theshadow 1.1     except KeyboardInterrupt:
241                       # ^C to exit.. 
242                       pass 
243                   if not d.done:
244                       d.failed()
245               
246               def prepare_display():
247                   scrwin.border(ord('|'),ord('|'),ord('-'),ord('-'),ord(' '),ord(' '),ord(' '),ord(' '))
248                   labelwin.addstr(0, 0, 'file:')
249                   labelwin.addstr(1, 0, 'size:')
250                   labelwin.addstr(2, 0, 'dest:')
251                   labelwin.addstr(3, 0, 'progress:')
252                   labelwin.addstr(4, 0, 'status:')
253                   labelwin.addstr(5, 0, 'dl speed:')
254                   labelwin.addstr(6, 0, 'ul speed:')
255 theshadow 1.2     labelwin.addstr(7, 0, 'sharing:')
256                   labelwin.addstr(8, 0, 'seeds:')
257                   labelwin.addstr(9, 0, 'peers:')
258 theshadow 1.7 #    labelwin.addstr(10, 0, '')
259               #    labelwin.addstr(11, 0, 'error(s):')
260 theshadow 1.1     curses.panel.update_panels()
261                   curses.doupdate()
262               
263               try:
264                   import curses
265                   import curses.panel
266               
267                   scrwin = curses.initscr()
268                   curses.noecho()
269                   curses.cbreak()
270               
271               except:
272                   print 'Textmode GUI initialization failed, cannot proceed.'
273                   print
274                   print 'This download interface requires the standard Python module ' \
275                      '"curses", which is unfortunately not available for the native ' \
276                      'Windows port of Python. It is however available for the Cygwin ' \
277                      'port of Python, running on all Win32 systems (www.cygwin.com).'
278                   print
279                   print 'You may still use "btdownloadheadless.py" to download.'
280               
281 theshadow 1.1 scrh, scrw = scrwin.getmaxyx()
282               scrpan = curses.panel.new_panel(scrwin)
283 theshadow 1.7 labelh, labelw, labely, labelx = 13, 9, 1, 2
284 theshadow 1.1 labelwin = curses.newwin(labelh, labelw, labely, labelx)
285               labelpan = curses.panel.new_panel(labelwin)
286 theshadow 1.7 fieldh, fieldw, fieldy, fieldx = labelh, scrw - 2 - labelw - 3, 1, labelw + 3
287 theshadow 1.1 fieldwin = curses.newwin(fieldh, fieldw, fieldy, fieldx)
288               fieldpan = curses.panel.new_panel(fieldwin)
289 theshadow 1.7 spewh, speww, spewy, spewx = scrh - labelh - 2, scrw - 3, 1 + labelh, 2
290               spewwin = curses.newwin(spewh, speww, spewy, spewx)
291               spewpan = curses.panel.new_panel(spewwin)
292 theshadow 1.1 prepare_display()
293               
294               signal(SIGWINCH, winch_handler)
295               
296               if __name__ == '__main__':
297                   mainerrlist = []
298                   try:
299                       run(mainerrlist, argv[1:])
300                   finally:
301 theshadow 1.7         try:
302                           curses.nocbreak()
303                           curses.echo()
304                       except:
305                           pass
306                       try:
307                           curses.endwin()
308                       except:
309                           pass
310 theshadow 1.1     if len(mainerrlist) != 0:
311                      print "These errors occurred during execution:"
312                      for error in mainerrlist:
313                         print error

No CVS admin address has been configured
Powered by
ViewCVS 0.9.3