|
#!/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 anydbm from stat import * #from common import * # FIXME import common for torrent.xml define? from BitTornado.bencode import bdecode 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 = '.torrents.xml' statsdbmFile = '.stats.db' dbmstats = None livestats = {} owners = {} torrentNames = {} outputFile = None def __init__(self,basedir): self.statsdbmFile = os.path.join(basedir,'.stats.db') self.outputXMLFile = os.path.join(basedir, '.torrents.xml') self.dbmstats = anydbm.open( self.statsdbmFile, 'c' ) 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 mergeStats(self,key): (totUp, totDn) = self.mergedStats(key) self.dbmstats[key] = '%d:%d' % (totUp,totDn) def saveStats(self): # merge livestats with dbmstats # FIXME this may have already happened; 'drop' msgs sent when shutting down for key in self.livestats: self.mergeStats(key) self.dbmstats.close() os.unlink(self.outputXMLFile) def display(self, data): try: outputFile = open( self.outputXMLFile, 'w' ) outputFile.write( '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n' ) outputFile.write( '<torrents>\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] key = '%s_%s' % (hash, self.owners.get(hash,'0')) self.livestats[key] = '%s:%s' % (liveUp,liveDn) (upamt,dnamt) = self.mergedStats(key) t = hours(t) if not t: t = status outputFile.write( '\t<torrent>\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,'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', uprate) self.printXML(outputFile,'downloadRate', 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</torrent>\n' ) outputFile.write( '</torrents>\n' ) #outputFile.flush() #outputFile.seek(0) outputFile.close() except: self.message( 'Failed to write output XML!') self.message( sys.exc_info[0] ) # tell launchmany not to stop return False def message(self, s): if s.startswith( "added" ): (msg,path) = s.replace('"','').split( ' ') (hash,ext) = os.path.basename(path).split('.') ownerUID = os.stat(path).st_uid self.owners[hash] = str(ownerUID) # FIXME grab torrent name here, emit in .torrents.xml name = nameFromTorrent(path) if name: self.torrentNames[hash] = name else: name = 'Unavailable' print '%d added torrent [%s] hash=%s' % (ownerUID, name, hash) elif s.startswith( "dropped" ): (msg,path) = s.replace('"','').split( ' ') (hash,ext) = os.path.basename(path).split('.') key = '%s_%s' % (hash, self.owners.get(hash,'0')) self.mergeStats(key) del self.owners[hash] del self.livestats[key] print 'Stopped torrent \'%s\' [%s]' % (path,key) ## shutting down msg launched PRIOR to stopping torrents #elif s == 'shutting down': # #self.saveStats() # print '!!! EXITING !!!' else: print "### "+s def printXML(self, fh, tag, value): fh.write( '\t\t<%s>%s</%s>' % (tag, value, tag) ) fh.write( '\n' ) def exception(self, s): Exceptions.append(s) self.message('SYSTEM ERROR - EXCEPTION GENERATED') self.message( str(s) ) if __name__ == '__main__': if argv[1:] == ['--version']: print version exit(0) defaults.extend( [ ( 'parse_dir_interval', 60, "how often to rescan the torrent directory, in seconds" ), ( 'saveas_style', 1, "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 <directory> <global options>\n" print "<directory> - 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'] ) LaunchMany(config, xmlDisplayer) if Exceptions: print '\nEXCEPTION:' print Exceptions[0] print 'please report this to '+report_email xmlDisplayer.saveStats()
|