assert sys.version >= '2', "Install Python 2.0 or greater" try: True except: True = 1 False = 0 PROFILER = False def hours(n): if n == 0: return 'complete!' try: n = int(n) assert n >= 0 and n < 5184000 # 60 days except: return '' m, s = divmod(n, 60) h, m = divmod(m, 60) if h > 0: return '%d hour %02d min %02d sec' % (h, m, s) else: return '%d min %02d sec' % (m, s) class HeadlessDisplayer: def __init__(self): self.done = False self.file = '' self.percentDone = '' self.timeEst = '' self.downloadTo = '' self.downRate = '' self.upRate = '' self.shareRating = '' self.seedStatus = '' self.peerStatus = '' self.errors = [] self.last_update_time = -1 def finished(self): self.done = True self.percentDone = '100' self.timeEst = 'Download Succeeded!' self.downRate = '' self.display() def failed(self): self.done = True self.percentDone = '0' self.timeEst = 'Download Failed!' self.downRate = '' self.display() def error(self, errormsg): self.errors.append(errormsg) self.display() def display(self, dpflag = Event(), fractionDone = None, timeEst = None, downRate = None, upRate = None, activity = None, statistics = None, **kws): if self.last_update_time + 0.1 > clock() and fractionDone not in (0.0, 1.0) and activity is not None: return self.last_update_time = clock() if fractionDone is not None: self.percentDone = str(float(int(fractionDone * 1000)) / 10) if timeEst is not None: self.timeEst = hours(timeEst) if activity is not None and not self.done: self.timeEst = activity if downRate is not None: self.downRate = '%.1f kB/s' % (float(downRate) / (1 << 10)) if upRate is not None: self.upRate = '%.1f kB/s' % (float(upRate) / (1 << 10)) if statistics is not None: if (statistics.shareRating < 0) or (statistics.shareRating > 100): self.shareRating = 'oo (%.1f MB up / %.1f MB down)' % (float(statistics.upTotal) / (1<<20), float(statistics.downTotal) / (1<<20)) else: self.shareRating = '%.3f (%.1f MB up / %.1f MB down)' % (statistics.shareRating, float(statistics.upTotal) / (1<<20), float(statistics.downTotal) / (1<<20)) if not self.done: self.seedStatus = '%d seen now, plus %.3f distributed copies' % (statistics.numSeeds,0.001*int(1000*statistics.numCopies)) else: self.seedStatus = '%d seen recently, plus %.3f distributed copies' % (statistics.numOldSeeds,0.001*int(1000*statistics.numCopies)) self.peerStatus = '%d seen now, %.1f%% done at %.1f kB/s' % (statistics.numPeers,statistics.percentDone,float(statistics.torrentRate) / (1 << 10)) print '\n\n\n\n' for err in self.errors: print 'ERROR:\n' + err + '\n' print 'saving: ', self.file print 'percent done: ', self.percentDone print 'time left: ', self.timeEst print 'download to: ', self.downloadTo print 'download rate: ', self.downRate print 'upload rate: ', self.upRate print 'share rating: ', self.shareRating print 'seed status: ', self.seedStatus print 'peer status: ', self.peerStatus stdout.flush() dpflag.set() def chooseFile(self, default, size, saveas, dir): self.file = '%s (%.1f MB)' % (default, float(size) / (1 << 20)) if saveas != '': default = saveas self.downloadTo = abspath(default) return default def newpath(self, path): self.downloadTo = path def run(params): try: import curses curses.initscr() cols = curses.COLS curses.endwin() except: cols = 80 h = HeadlessDisplayer() while 1: configdir = ConfigDir('downloadheadless') defaultsToIgnore = ['responsefile', 'url', 'priority'] configdir.setDefaults(defaults,defaultsToIgnore) configdefaults = configdir.loadConfig() defaults.append(('save_options',0, "whether to save the current options as the new default configuration " + "(only for btdownloadheadless.py)")) try: config = parse_params(params, configdefaults) except ValueError, e: print 'error: ' + str(e) + '\nrun with no args for parameter explanations' break if not config: print get_usage(defaults, 80, configdefaults) break if config['save_options']: configdir.saveConfig(config) configdir.deleteOldCacheData(config['expire_cache_data']) myid = createPeerID() seed(myid) # Arno: FIXME: I temporarily disable the overlay swarm for this # short-lived client. In the future we could enable the overlay. GLOBAL.do_overlay = 0 Encrypter.option_pattern = Encrypter.disabled_overlay_option_pattern doneflag = Event() def disp_exception(text): print text rawserver = RawServer(doneflag, config['timeout_check_interval'], config['timeout'], ipv6_enable = config['ipv6_enabled'], failfunc = h.failed, errorfunc = disp_exception) upnp_type = UPnP_test(config['upnp_nat_access']) while True: try: listen_port = rawserver.find_and_bind(config['minport'], config['minport'], config['maxport'], config['bind'], ipv6_socket_style = config['ipv6_binds_v4'], upnp = upnp_type, randomizer = config['random_port']) break except socketerror, e: if upnp_type and e == UPnP_ERROR: print 'WARNING: COULD NOT FORWARD VIA UPnP' upnp_type = 0 continue print "error: Couldn't listen - " + str(e) h.failed() return response = get_response(config['responsefile'], config['url'], h.error) if not response: break infohash = sha(bencode(response['info'])).digest() dow = BT1Download(h.display, h.finished, h.error, disp_exception, doneflag, config, response, infohash, myid, rawserver, listen_port, configdir) if not dow.saveAs(h.chooseFile, h.newpath): break if not dow.initFiles(old_style = True): break if not dow.startEngine(): dow.shutdown() break dow.startRerequester() dow.autoStats() if not dow.am_I_finished(): h.display(activity = 'connecting to peers') rawserver.listen_forever(dow.getPortHandler()) h.display(activity = 'shutting down') dow.shutdown() break try: rawserver.shutdown() except: pass if not h.done: h.failed() if __name__ == '__main__': if argv[1:] == ['--version']: print version sys.exit(0) if PROFILER: import profile, pstats p = profile.Profile() p.runcall(run, argv[1:]) log = open('profile_data.'+strftime('%y%m%d%H%M%S')+'.txt','a') normalstdout = sys.stdout sys.stdout = log # pstats.Stats(p).strip_dirs().sort_stats('cumulative').print_stats() pstats.Stats(p).strip_dirs().sort_stats('time').print_stats() sys.stdout = normalstdout else: run(argv[1:])