Blame | Last modification | View Log | Download
#!/usr/bin/env python# Written by Bram Cohen# see LICENSE.txt for license informationfrom BitTornado import PSYCOif PSYCO.psyco:try:import psycoassert psyco.__version__ >= 0x010100f0psyco.full()except:passfrom BitTornado.download_bt1 import BT1Download, defaults, parse_params, get_usage, get_responsefrom BitTornado.RawServer import RawServer, UPnP_ERRORfrom random import seedfrom socket import error as socketerrorfrom BitTornado.bencode import bencodefrom BitTornado.natpunch import UPnP_testfrom threading import Eventfrom os.path import abspathfrom os import getpid, removefrom sys import argv, stdoutimport sysfrom sha import shafrom time import strftimefrom BitTornado.clock import clockfrom BitTornado import createPeerID, versionassert sys.version >= '2', "Install Python 2.0 or greater"try:Trueexcept:True = 1False = 0PROFILER = Falseif __debug__: LOGFILE=open(argv[3]+"."+str(getpid()),"w")def traceMsg(msg):try:if __debug__:LOGFILE.write(msg + "\n")LOGFILE.flush()except:returndef hours(n):if n == 0:return 'complete!'try:n = int(n)assert n >= 0 and n < 5184000 # 60 daysexcept:return '<unknown>'m, s = divmod(n, 60)h, m = divmod(m, 60)d, h = divmod(h, 24)if d > 0:return '%dd %02d:%02d:%02d' % (d, h, m, s)else:return '%02d:%02d:%02d' % (h, m, s)class HeadlessDisplayer:def __init__(self):self.done = Falseself.file = ''self.percentDone = ''self.timeEst = 'Connecting to Peers'self.downloadTo = ''self.downRate = ''self.upRate = ''self.shareRating = ''self.percentShare = ''self.upTotal = 0self.downTotal = 0self.seedStatus = ''self.peerStatus = ''self.seeds = ''self.peers = ''self.errors = []self.last_update_time = -1self.statFile = 'percent.txt'self.autoShutdown = 'False'self.user = 'unknown'self.size = 0self.shareKill = '100'self.distcopy = ''self.stoppedAt = ''def finished(self):if __debug__: traceMsg('finished - begin')self.done = Trueself.percentDone = '100'self.timeEst = 'Download Succeeded!'self.downRate = ''self.display()if self.autoShutdown == 'True':self.upRate = ''if self.stoppedAt == '':self.writeStatus()if __debug__: traceMsg('finished - end - raised ki')raise KeyboardInterruptif __debug__: traceMsg('finished - end')def failed(self):if __debug__: traceMsg('failed - begin')self.done = Trueself.percentDone = '0'self.timeEst = 'Download Failed!'self.downRate = ''self.display()if self.autoShutdown == 'True':self.upRate = ''if self.stoppedAt == '':self.writeStatus()if __debug__:traceMsg('failed - end - raised ki')raise KeyboardInterruptif __debug__: traceMsg('failed - end')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 __debug__: traceMsg('display - begin')if self.last_update_time + 0.1 > clock() and fractionDone not in (0.0, 1.0) and activity is not None:returnself.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 = activityif 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))self.downTotal = statistics.downTotalself.upTotal = statistics.upTotalelse:self.shareRating = '%.3f (%.1f MB up / %.1f MB down)' % (statistics.shareRating, float(statistics.upTotal) / (1<<20), float(statistics.downTotal) / (1<<20))self.downTotal = statistics.downTotalself.upTotal = statistics.upTotalif not self.done:self.seedStatus = '%d seen now, plus %.3f distributed copies' % (statistics.numSeeds,0.001*int(1000*statistics.numCopies))self.seeds = (str(statistics.numSeeds))else:self.seedStatus = '%d seen recently, plus %.3f distributed copies' % (statistics.numOldSeeds,0.001*int(1000*statistics.numCopies))self.seeds = (str(statistics.numOldSeeds))self.peers = '%d' % (statistics.numPeers)self.distcopy = '%.3f' % (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))dpflag.set()if __debug__: traceMsg('display - prior to self.write')if self.stoppedAt == '':self.writeStatus()if __debug__: traceMsg('display - end')def chooseFile(self, default, size, saveas, dir):if __debug__: traceMsg('chooseFile - begin')self.file = '%s (%.1f MB)' % (default, float(size) / (1 << 20))self.size = sizeif saveas != '':default = saveasself.downloadTo = abspath(default)if __debug__: traceMsg('chooseFile - end')return defaultdef writeStatus(self):if __debug__: traceMsg('writeStatus - begin')downRate = self.downTotaldie = Falsetry:f=open(self.statFile,'r')running = f.read(1)f.closeexcept:running = 0self.timeEst = 'Failed To Open StatFile'if __debug__: traceMsg('writeStatus - Failed to Open StatFile')if __debug__: traceMsg('writeStatus - running :' + str(running))if __debug__: traceMsg('writeStatus - stoppedAt :' + self.stoppedAt)if running == '0':if self.stoppedAt == '':if self.percentDone == '100':self.stoppedAt = '100'else:self.stoppedAt = str((float(self.percentDone)+100)*-1)self.timeEst = 'Torrent Stopped'die = Trueself.upRate = ''self.downRate = ''self.percentDone = self.stoppedAtelse:if downRate == 0 and self.upTotal > 0:downRate = self.sizeif self.done:self.percentDone = '100'downRate = self.sizeif self.autoShutdown == 'True':running = '0'if self.upTotal > 0:self.percentShare = '%.1f' % ((float(self.upTotal)/float(downRate))*100)else:self.percentShare = '0.0'if self.done and self.percentShare is not '' and self.autoShutdown == 'False':if (float(self.percentShare) >= float(self.shareKill)) and (self.shareKill != '0'):die = Truerunning = '0'self.upRate = ''elif (not self.done) and (self.timeEst == 'complete!') and (self.percentDone == '100.0'):if (float(self.percentShare) >= float(self.shareKill)) and (self.shareKill != '0'):die = Truerunning = '0'self.upRate = ''#self.finished()lcount = 0while 1:lcount += 1try:f=open(self.statFile,'w')f.write(running + '\n')f.write(self.percentDone + '\n')f.write(self.timeEst + '\n')f.write(self.downRate + '\n')f.write(self.upRate + '\n')f.write(self.user + '\n')f.write(self.seeds + '+' + self.distcopy + '\n')f.write(self.peers + '\n')f.write(self.percentShare + '\n')f.write(self.shareKill + '\n')f.write(str(self.upTotal) + '\n')f.write(str(self.downTotal) + '\n')f.write(str(self.size))try:errs = []errs = self.scrub_errs()for errmsg in errs:f.write('\n' + errmsg)except:if __debug__: traceMsg('writeStatus - Failed during writing Errors')passf.flush()f.close()breakexcept:if __debug__: traceMsg('writeStatus - Failed to Open StatFile for Writing')if lcount > 30:breakpassif die:if __debug__: traceMsg('writeStatus - dieing - raised ki')raise KeyboardInterruptdef newpath(self, path):self.downloadTo = pathdef scrub_errs(self):new_errors = []try:if self.errors:last_errMsg = ''errCount = 0for err in self.errors:try:if last_errMsg == '':last_errMsg = errelif last_errMsg == err:errCount += 1elif last_errMsg != err:if errCount > 0:new_errors.append(last_errMsg + ' (x' + str(errCount+1) + ')')else:new_errors.append(last_errMsg)errCount = 0last_errMsg = errexcept:if __debug__: traceMsg('scrub_errs - Failed scrub')passtry:if len(new_errors) > 0:if last_errMsg != new_errors[len(new_errors)-1]:if errCount > 0:new_errors.append(last_errMsg + ' (x' + str(errCount+1) + ')')else:new_errors.append(last_errMsg)else:if errCount > 0:new_errors.append(last_errMsg + ' (x' + str(errCount+1) + ')')else:new_errors.append(last_errMsg)except:if __debug__: traceMsg('scrub_errs - Failed during scrub last Msg ')passif len(self.errors) > 100:while len(self.errors) > 100 :del self.errors[0:99]self.errors = new_errorsexcept:if __debug__: traceMsg('scrub_errs - Failed during scrub Errors')passreturn new_errorsdef run(autoDie,shareKill,statusFile,userName,params):if __debug__: traceMsg('run - begin')try:f=open(statusFile+".pid",'w')f.write(str(getpid()).strip() + "\n")f.flush()f.close()except:if __debug__: traceMsg('run - Failed to Create PID file')passtry:h = HeadlessDisplayer()h.statFile = statusFileh.autoShutdown = autoDieh.shareKill = shareKillh.user = userNamewhile 1:try:config = parse_params(params)except ValueError, e:print 'error: ' + str(e) + '\nrun with no args for parameter explanations'breakif not config:print get_usage()breakmyid = createPeerID()seed(myid)doneflag = Event()def disp_exception(text):print textrawserver = 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['maxport'],config['bind'], ipv6_socket_style = config['ipv6_binds_v4'],upnp = upnp_type, randomizer = config['random_port'])breakexcept socketerror, e:if upnp_type and e == UPnP_ERROR:print 'WARNING: COULD NOT FORWARD VIA UPnP'upnp_type = 0continueprint "error: Couldn't listen - " + str(e)h.failed()returnresponse = get_response(config['responsefile'], config['url'], h.error)if not response:breakinfohash = sha(bencode(response['info'])).digest()dow = BT1Download(h.display, h.finished, h.error, disp_exception, doneflag,config, response, infohash, myid, rawserver, listen_port)if not dow.saveAs(h.chooseFile, h.newpath):breakif not dow.initFiles(old_style = True):breakif not dow.startEngine():dow.shutdown()breakdow.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()breaktry:rawserver.shutdown()except:passif not h.done:h.failed()finally:if __debug__: traceMsg('run - removing PID file :'+statusFile+".pid")remove(statusFile+".pid")if __debug__: traceMsg('run - end')if __name__ == '__main__':if argv[1:] == ['--version']:print versionsys.exit(0)if PROFILER:import profile, pstatsp = profile.Profile()p.runcall(run, argv[1],argv[2],argv[3],argv[4],argv[5:])log = open('profile_data.'+strftime('%y%m%d%H%M%S')+'.txt','a')normalstdout = sys.stdoutsys.stdout = log# pstats.Stats(p).strip_dirs().sort_stats('cumulative').print_stats()pstats.Stats(p).strip_dirs().sort_stats('time').print_stats()sys.stdout = normalstdoutelse:run(argv[1],argv[2],argv[3],argv[4],argv[5:])