All About Me
 Old Crap
 What's Going On
   Important News
 Pictures
   Kitty Porn

 Things I can't live without

> Cars
> Computers
> Food/Recipes
> Humor
> Music
> Privacy
Expand your vocabulary and feed a hungry person!

#!/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()


(© 1996-2008. Please don't link to images here; bandwidth costs money. Thanks.)