#!/usr/local/bin/python ######################################################################## # btlaunchmany script to emit XML # and log statistics ######################################################################## from BitTornado import PSYCO if PSYCO.psyco: try: import psyco assert psyco.__version__ >= 0x010100f0 psyco.full() except: pass from BitTornado.launchmanycore import LaunchMany from BitTornado.download_bt1 import defaults, get_usage from BitTornado.parseargs import parseargs from threading import Event from sys import argv, exit import sys, os from BitTornado import version, report_email from BitTornado.ConfigDir import ConfigDir import os.path import shelve import time import shutil from stat import * from common import * from BitTornado.bencode import bdecode from pysqlite2 import dbapi2 as sqlite assert sys.version >= '2', "Install Python 2.0 or greater" try: True except: True = 1 False = 0 def nameFromTorrent(fn): try: f = open( fn, 'rb' ) metaInfo = bdecode( f.read()) f.close() info = metaInfo['info'] return info['name'] except: return '' def hours(n): if n == 0: return 'complete!' try: n = int(n) assert n >= 0 and n < 5184000 # 60 days except: return 'unknown' 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) Exceptions = [] class XMLDisplayer: outputXMLFile = TORRENT_XML dbmstats = {} livestats = {} owners = {} torrentNames = {} torrentDates = {} outputFile = None def __init__(self,basedir): dataDir = os.path.join(basedir,'.data') self.statsRecorder = SqliteStats(MASTER_HASH_LIST) self._log=MessageLogger('launchmany') self._log.printmsg('Starting...') def mergedStats(self,key): liveValue = self.livestats.get(key,'0:0') dbmValue = self.dbmstats.get(key,'0:0') (liveUp,liveDn) = liveValue.split(':') (dbmUp,dbmDn) = dbmValue.split(':') totUp = int(liveUp) + int(dbmUp) totDn = int(liveDn) + int(dbmDn) return (totUp,totDn) def cleanup(self): self.saveStats() os.unlink(self.outputXMLFile) self.statsRecorder.close() self._log.printmsg('STOPPING...') self._log.close() def saveStats(self): for hash in self.livestats: (up,dn) = self.mergedStats(hash) uid = self.owners[hash] self.saveStatsForHashAndUser(hash,uid,uploaded=up,downloaded=dn) def display(self, data): try: outputFile = SafeWriteFile(self.outputXMLFile,0640) outputFile.write( '\n' ) outputFile.write( '\n' ) totalBytesUp = 0 totalBytesDn = 0 totalSpeedUp = 0 totalSpeedDn = 0 totalIncoming = 0 if data: for x in data: ( fullPath, status, progress, peers, seeds, seedsmsg, dist, uprate, dnrate, liveUp, liveDn, size, t, msg ) = x # find stats in our dbm hash = os.path.basename(fullPath).split('.')[0] self.livestats[hash] = '%s:%s' % (liveUp,liveDn) (upamt,dnamt) = self.mergedStats(hash) t = hours(t) if not t: t = status outputFile.write( '\t\n' ) self.printXML(outputFile, 'name', self.torrentNames.get(hash, hash)) self.printXML(outputFile, 'hash', hash) self.printXML(outputFile, 'fullpath', fullPath) self.printXML(outputFile, 'owner', self.owners.get(hash,'0')) self.printXML(outputFile, 'started', self.torrentDates.get(hash,'0') ) self.printXML(outputFile,'status',status) self.printXML(outputFile,'progress', progress) self.printXML(outputFile,'peers', peers) self.printXML(outputFile,'seeds',seeds) self.printXML(outputFile,'seedmsg', seedsmsg) self.printXML(outputFile,'distcopies', dist) self.printXML(outputFile,'uploadRate', float(uprate)) self.printXML(outputFile,'downloadRate', float(dnrate)) # store session (livestats[]) up/dn bytes self.printXML(outputFile, 'sessionUploadBytes', liveUp) self.printXML(outputFile, 'sessionDownloadBytes', liveDn) # totalUploadBytes, totalDnBytes self.printXML(outputFile,'totalUploadBytes', upamt) self.printXML(outputFile,'totalDownloadBytes', dnamt) self.printXML(outputFile,'filesize', size) self.printXML(outputFile,'eta', t) self.printXML(outputFile,'msg', msg) outputFile.write( '\t\n' ) outputFile.write( '\n' ) outputFile.close() except: self.message( 'Failed to write output XML!') #self.message( sys.exc_info[0] ) # save our stats every go-around self.saveStats() # tell launchmany not to stop return False def saveStatsForHashAndUser(self,hash,uid,uploaded=0,downloaded=0): self.statsRecorder.saveStatsForHashAndUser(hash,uid,uploaded,downloaded) def getStoredStatsForHashAndUser(self,hash,uid): return self.statsRecorder.getStoredStatsForHashAndUser(hash,uid) def addTorrent(self,s): (msg,path) = s.replace('"','').split( ' ') (hash,ext) = os.path.basename(path).split('.') fileStat = os.stat(path) ownerUID = fileStat.st_uid self.owners[hash] = str(ownerUID) name = nameFromTorrent(path) if name: self.torrentNames[hash] = name else: name = 'Unavailable' # get date added self.torrentDates[hash] = fileStat.st_ctime storedUp,storedDn = self.getStoredStatsForHashAndUser(hash, ownerUID) self.dbmstats[hash] = '%d:%d' % (storedUp,storedDn) self.printlog( '%d added torrent [%s] hash=%s' % (ownerUID, name, hash) ) def dropTorrent(self,s): (msg,path) = s.replace('"','').split( ' ') (hash,ext) = os.path.basename(path).split('.') uid = self.owners.get(hash,'0') ## save our statistics (totUp,totDn) = self.mergedStats(hash) self.saveStatsForHashAndUser(hash, uid, uploaded=totUp, downloaded=totDn) ## wipe all of our lookups torrentName = self.torrentNames.get(hash,'UNKNOWN') del self.dbmstats[hash] del self.livestats[hash] del self.owners[hash] del self.torrentNames[hash] del self.torrentDates[hash] self.printlog( 'Stopped torrent \'%s\' [%s]' % (torrentName,hash)) def message(self, s): if s.startswith( "added" ): self.addTorrent(s) elif s.startswith( "dropped" ): self.dropTorrent(s) else: self.printlog( s ) def printXML(self, fh, tag, value): if tag == 'name': fh.write( '\t\t<%s>' % (tag, value, tag) ) else: fh.write( '\t\t<%s>%s' % (tag, value, tag) ) fh.write( '\n' ) def exception(self, s): Exceptions.append(s) self.message('EXCEPTION CAUGHT: %s' % s) def printlog(self,msg): self._log.printmsg(msg) if __name__ == '__main__': if argv[1:] == ['--version']: print version exit(0) defaults.extend( [ ( 'parse_dir_interval', 20, "how often to rescan the torrent directory, in seconds" ), ( 'saveas_style', 3, "How to name torrent downloads (1 = rename to torrent name, " + "2 = save under name in torrent, 3 = save in directory under torrent name)" ), ( 'display_path', 1, "whether to display the full path or the torrent contents for each torrent" ), ] ) try: configdir = ConfigDir('launchmany') 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 btlaunchmany.py)")) if len(argv) < 2: print "Usage: btlaunchmany.py \n" print " - directory to look for .torrent files (semi-recursive)" print get_usage(defaults, 80, configdefaults) exit(1) config, args = parseargs(argv[1:], defaults, 1, 1, configdefaults) if config['save_options']: configdir.saveConfig(config) configdir.deleteOldCacheData(config['expire_cache_data']) if not os.path.isdir(args[0]): raise ValueError("Warning: "+args[0]+" is not a directory") config['torrent_dir'] = args[0] except ValueError, e: print 'error: ' + str(e) + '\nrun with no args for parameter explanations' exit(1) # properly instantiate XMLDisplayer, pass it off as a variable xmlDisplayer = XMLDisplayer( config['torrent_dir'] ) try: LaunchMany(config, xmlDisplayer) finally: xmlDisplayer.cleanup()