home *** CD-ROM | disk | FTP | other *** search
/ Mundo do CD-ROM 118 / cdrom118.iso / internet / webaroo / WebarooSetup.exe / Webaroo.msi / _A0DEB44B94924E89917E71AA90C5F226 / btdownloader.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2006-04-03  |  16.9 KB  |  552 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.4)
  3.  
  4. from __future__ import division
  5. import gettext
  6. gettext.install('bittorrent', 'locale')
  7. import sys
  8. import os
  9. import threading
  10. import glob
  11. import random
  12. import shutil
  13. from time import time, strftime
  14. from cStringIO import StringIO
  15. from encodings import *
  16. import urllib2
  17. import urlparse
  18. import os.path as os
  19. from BitTorrent.download import Feedback, Multitorrent
  20. from BitTorrent.defaultargs import get_defaults
  21. from BitTorrent.bencode import bdecode
  22. from BitTorrent.ConvertedMetainfo import ConvertedMetainfo
  23. from BitTorrent import configfile, BTFailure
  24. import tempfile
  25. dirname = tempfile.gettempdir()
  26. tmp_dir = dirname + '/webaroo/'
  27. if not os.path.isdir(tmp_dir):
  28.     os.mkdir(tmp_dir)
  29.  
  30. _logfile = tmp_dir + 'btdownloader.log'
  31. import logging
  32. import logging.handlers as logging
  33. logger = logging.getLogger('btdownloader')
  34. hdlr = logging.handlers.RotatingFileHandler(_logfile, maxBytes = 1024 * 1024, backupCount = 10)
  35. hdlr.setFormatter(logging.Formatter('%(asctime)s %(message)s'))
  36. logger.addHandler(hdlr)
  37. logger.setLevel(logging.INFO)
  38. MAX_SEED_FILES_COUNT = 4
  39. MAX_DOWNLOAD_FILES_COUNT = 4
  40.  
  41. class DL(Feedback):
  42.     ''' DL class
  43. \t\tThis class is derived from Feedback, "hosts" Multitorrent, and it runs 
  44. \t\tin a seperate thread. Error and other messages from the multitorrent are
  45. \t\trecieved by respective members of this class.
  46. \t'''
  47.     
  48.     def __init__(self, store):
  49.         logger.warn(_('Initializing DL'))
  50.         self.store = store
  51.         self.store.doneflag = threading.Event()
  52.         self.multitorrent = Multitorrent(self.store.config, self.store.doneflag, self.global_error)
  53.         self.last_error = None
  54.         self.count_error = 0
  55.         self.last_error_print_time = time()
  56.  
  57.     
  58.     def run(self):
  59.         logger.info('Starting Download thread.')
  60.         self.multitorrent.rawserver.listen_forever()
  61.         self.coreFinished.set()
  62.         logger.info('Downloads done, shutting down bittorrent core.')
  63.  
  64.     
  65.     def global_error(self, level, text):
  66.         self._log_error(text)
  67.  
  68.     
  69.     def error(self, torrent, level, text):
  70.         self._log_error(text)
  71.  
  72.     
  73.     def failed(self, torrent, is_external):
  74.         logger.error('Failed!!!')
  75.  
  76.     
  77.     def finished(self, torrent):
  78.         logger.warn('Finished')
  79.  
  80.     
  81.     def _log_error(self, msg):
  82.         if msg == self.last_error:
  83.             self.count_error += 1
  84.             if time() - self.last_error_print_time >= 5 and self.count_error:
  85.                 logger.error('Last error repeated %d times.' % self.count_error)
  86.                 self.last_error_print_time = time()
  87.             
  88.         elif self.count_error:
  89.             logger.error('Error repeated %d times: %s' % (self.count_error, self.last_error))
  90.         
  91.         logger.error('Error: %s' % msg)
  92.         self.last_error = msg
  93.         self.count_error = 0
  94.         self.last_error_print_time = time()
  95.  
  96.  
  97.  
  98. class DLThread(threading.Thread):
  99.     ''' DLThread class
  100. \t\tThis class is instantiated once in the __init__ of BTDownloader, used 
  101. \t\tto run a thread for downloading.  
  102. \t'''
  103.     
  104.     def __init__(self):
  105.         threading.Thread.__init__(self)
  106.         logger.warn('Initializing thread.')
  107.  
  108.     
  109.     def run(self):
  110.         logger.warn('DLThread.run called.')
  111.         self.dl.run()
  112.  
  113.  
  114.  
  115. class Store:
  116.     ''' Store class.
  117. \t\tMost of the data used in this module are stored in this store.
  118.  
  119. \t\tMain elements are:
  120. \t\tself.torrents: a list containg tuple of torrent, metainfo and file name 
  121. \t\t               being downloaded.
  122. \t\tself.dl: an instance of DL.
  123. \t\tself.dlthread: an instance of DLThread.
  124. \t'''
  125.     
  126.     def __init__(self):
  127.         self.torrents = []
  128.         self.seeding_torrents = []
  129.         self.uiname = 'btdownloadcurses'
  130.         self.defaults = get_defaults(self.uiname)
  131.         
  132.         try:
  133.             (self.config, args) = configfile.parse_configuration_and_args(self.defaults, self.uiname, [], 0, 1, tmp_dir)
  134.         except BTFailure:
  135.             e = None
  136.             logger.error('Parsing arguments failed(%s). Bailing out.' % str(e))
  137.             sys.exit(1)
  138.  
  139.  
  140.  
  141.  
  142. class BTDownloader:
  143.     ''' class BTDownloader
  144. \t\tThis is the main class, can be registered as a COM server or can be 
  145. \t\tused as standard python module.
  146.  
  147. \t\tCreating an instance of this class starts a bittorrent core in a 
  148. \t\tseperate thread. No clean up is required at the end, it will 
  149. \t\tautomatically shutdown everything cleanly as far as possible when
  150. \t\tthe reference is freed up.
  151.  
  152. \t\tMost of the bittorrent related configuration and data are stored in a 
  153. \t\tclass Store instance self.store.\t\t
  154. \t'''
  155.     _public_methods_ = [
  156.         'getStatAll',
  157.         'setDataDir',
  158.         'add',
  159.         'remove',
  160.         'downloadsInProgress',
  161.         'finished',
  162.         'status',
  163.         'shutdown',
  164.         'seedCompletedFiles',
  165.         'stopSeedingFiles',
  166.         'remove_file']
  167.     _reg_progid_ = 'Webaroo.BTDownloader'
  168.     _reg_clsid_ = '{DB5B62B0-57BD-4915-8932-99CD5BEABFA2}'
  169.     
  170.     def __init__(self, rerequest_interval = 100, min_peers = 50, max_upload_rate = 0):
  171.         logger.warn('initializing com server')
  172.         
  173.         def handle_errors(f, p, e):
  174.             logger.error('Error while removing .bittorrent: %s, %s, %s' % (f, p, e))
  175.  
  176.         shutil.rmtree(os.path.join(tmp_dir, '.bittorrent'), onerror = handle_errors)
  177.         self.store = Store()
  178.         self.store.config['rerequest_interval'] = rerequest_interval
  179.         self.store.config['min_peers'] = min_peers
  180.         self.store.config['max_upload_rate'] = max_upload_rate
  181.         self.store.config['spew'] = True
  182.         self.store.config['timeout_check_interval'] = 5.0
  183.         self.store.config['fast_socket_timeout'] = 5.0
  184.         self.store.config['socket_timeout'] = 60.0
  185.         self.dl = DL(self.store)
  186.         self.dl.coreFinished = threading.Event()
  187.         self.dlthread = DLThread()
  188.         self.dlthread.dl = self.dl
  189.         self.dlthread.start()
  190.  
  191.     
  192.     def setDataDir(self, datadir):
  193.         logger.info('setDataDir: called with %s' % datadir)
  194.         self.datadir = datadir
  195.  
  196.     
  197.     def __del__(self):
  198.         logger.warn('Shutting down COM server.')
  199.         self.shutdown()
  200.  
  201.     
  202.     def getMetaInfo(self, url, location = None):
  203.         if location == None:
  204.             location = self.datadir
  205.         
  206.         tdata = ''
  207.         orig = ''
  208.         
  209.         try:
  210.             h = file(url, 'rb')
  211.             tdata = h.read()
  212.             h.close()
  213.             file_name = os.path.split(url)[1]
  214.             orig = file_name
  215.         except:
  216.             
  217.             try:
  218.                 tdata = urllib2.urlopen(url).read()
  219.             except Exception:
  220.                 e = None
  221.                 logger.error('Cant read file: %s[%s]' % (url, str(e)))
  222.  
  223.             file_name = os.path.split(urlparse.urlparse(url)[2])[1]
  224.             orig = file_name
  225.             
  226.             try:
  227.                 file(os.path.join(location, file_name), 'wb').write(tdata)
  228.  
  229.  
  230.         
  231.         try:
  232.             metainfo = ConvertedMetainfo(bdecode(tdata))
  233.         except BTFailure:
  234.             logger.info('Invalid torrent file(%s).' % url)
  235.             return (None, None)
  236.  
  237.         return (metainfo, orig)
  238.  
  239.     
  240.     def cleanupTorrents(self, seedOnly = False):
  241.         logger.info('cleanupTorrents called; seedOnly: %s' % seedOnly)
  242.         if seedOnly:
  243.             torrents = self.store.seeding_torrents
  244.             MAX_FILES_COUNT = MAX_SEED_FILES_COUNT
  245.         else:
  246.             torrents = self.store.torrents
  247.             MAX_FILES_COUNT = MAX_DOWNLOAD_FILES_COUNT
  248.         ntorrents = [](_[1])
  249.         for i in range(ntorrents):
  250.             stat = self.status(i)
  251.             if stat[1] == stat[2]:
  252.                 logger.info('cleanupTorrents: removing complete file %s' % stat[3])
  253.                 self.remove(i, seedOnly)
  254.                 return True
  255.                 continue
  256.             None if ntorrents < MAX_FILES_COUNT else len
  257.         
  258.         return False
  259.  
  260.     
  261.     def isSeeded(self, orig, seedOnly = False):
  262.         if seedOnly:
  263.             torrents = self.store.seeding_torrents
  264.         else:
  265.             torrents = self.store.torrents
  266.         for i in range(len(torrents)):
  267.             if torrents[i][3] == orig:
  268.                 if torrents[i][0]:
  269.                     if os.path.exists(torrents[i][2]):
  270.                         logger.warn('File %s already seeded and available. Returning id: %s;seedOnly: %s' % (orig, i, seedOnly))
  271.                         return i
  272.                     else:
  273.                         self.remove(i, seedOnly)
  274.                         return -1
  275.                 else:
  276.                     logger.warn('THIS SHOULD NEVER HAPPEN')
  277.                     return -1
  278.             torrents[i][0]
  279.         
  280.         return -1
  281.  
  282.     
  283.     def add(self, url, location = None, seedOnly = False):
  284.         ''' add torrent.
  285. \t\t\tThis function adds the torrent file specified in url to the 
  286. \t\t\tbittorrent core, and starts downloading it. If location is supplied 
  287. \t\t\tit will save the file in location directory.
  288. \t\t\t
  289. \t\t\tTo get status of download call status.      
  290. \t\t'''
  291.         logger.info('get called with: url = %s, location = %s' % (url, location))
  292.         if location is None:
  293.             location = self.datadir
  294.         
  295.         (metainfo, orig) = self.getMetaInfo(url, location)
  296.         if not metainfo:
  297.             return -1
  298.         
  299.         index = self.isSeeded(orig, seedOnly = True)
  300.         if index >= 0:
  301.             if not seedOnly:
  302.                 self.torrents.append(self.seeding_torrents[index])
  303.                 self.seeding_torrents[index] = [
  304.                     None,
  305.                     None,
  306.                     None,
  307.                     None]
  308.                 return len(self.torrents) - 1
  309.             else:
  310.                 return index
  311.         
  312.         index = self.isSeeded(orig)
  313.         if index >= 0:
  314.             logger.info('Received index for: url = %s, with id = %s' % (url, index))
  315.             return index
  316.         
  317.         if not self.cleanupTorrents(seedOnly):
  318.             logger.warn("cleanupTorrents returned false, can't que more torrents for now.")
  319.             return -1
  320.         
  321.         save_as = os.path.join(location, metainfo.name_fs)
  322.         
  323.         try:
  324.             logger.info('add: calling start_torrent with %s' % save_as)
  325.             torrent = self.dl.multitorrent.start_torrent(metainfo, self.store.config, self.dl, save_as)
  326.         except Exception:
  327.             e = None
  328.             logger.warn('Exception while starting torrent: %s' % str(e))
  329.             return -1
  330.  
  331.         if seedOnly:
  332.             self.store.seeding_torrents.append((torrent, metainfo, save_as, orig))
  333.             id = len(self.store.seeding_torrents) - 1
  334.         else:
  335.             self.store.torrents.append((torrent, metainfo, save_as, orig))
  336.             id = len(self.store.torrents) - 1
  337.         logger.info('add: returning with %s for %s[seedOnly: %s]' % (url, id, seedOnly))
  338.         return id
  339.  
  340.     
  341.     def remove(self, id, seedOnly = False):
  342.         if seedOnly:
  343.             torrents = self.store.seeding_torrents
  344.         else:
  345.             torrents = self.store.torrents
  346.         
  347.         try:
  348.             (torrent, meta, _file, orig) = torrents[id]
  349.             logger.warn('Shutting down %s, status: %s' % (orig, str(self.status(id))))
  350.             if torrent:
  351.                 torrent.shutdown()
  352.             
  353.             torrents[id] = (None, None, None, None)
  354.         except Exception:
  355.             e = None
  356.             logger.warn('Exception during shutting down torrent[%s]: %s' % (_file, str(e)))
  357.  
  358.  
  359.     
  360.     def isDownloadDone(self, torrent_file):
  361.         if not torrent_file.lower().endswith('.torrent'):
  362.             return False
  363.         
  364.         (metainfo, localname) = self.getMetaInfo(torrent_file)
  365.         if not metainfo:
  366.             return False
  367.         
  368.         local_filename = torrent_file[:-len('.torrent')]
  369.         size_as_per_torrent = metainfo.total_bytes
  370.         if os.path.exists(local_filename):
  371.             size_on_disk = os.path.getsize(local_filename)
  372.         else:
  373.             size_on_disk = 0
  374.         logger.info('isDownloadDone: [%s] on disk = %s; in metainfo = %s' % (torrent_file, size_on_disk, size_as_per_torrent))
  375.         return size_as_per_torrent == size_on_disk
  376.  
  377.     
  378.     def remove_file(self, name):
  379.         for i in range(len(self.store.seeding_torrents)):
  380.             metainfo = self.store.seeding_torrents[i][1]
  381.             if metainfo:
  382.                 if metainfo.name_fs == name:
  383.                     logger.info('remove_file: removing file %s' % name)
  384.                     self.remove(i, seedOnly = True)
  385.                     return None
  386.                 
  387.             metainfo.name_fs == name
  388.         
  389.         for i in range(len(self.store.torrents)):
  390.             metainfo = self.store.torrents[i][1]
  391.             if metainfo:
  392.                 if metainfo.name_fs == name:
  393.                     logger.info('remove_file: removing file %s' % name)
  394.                     self.remove(i)
  395.                     return None
  396.                 
  397.             metainfo.name_fs == name
  398.         
  399.         logger.info('remove_file: did not find any instace of the file')
  400.  
  401.     
  402.     def seedCompletedFiles(self):
  403.         '''Start seeding files already present in the given directory.'''
  404.         logger.info('seedCompletedFiles called, datadir: %s' % self.datadir)
  405.         torrents = glob.glob(os.path.join(self.datadir, '*.torrent'))
  406.         logger.info('Torrents in cache directory are: %r' % torrents)
  407.         torrents = filter(self.isDownloadDone, torrents)
  408.         logger.info('Finished torrents in cache directory are: %r' % torrents)
  409.         
  410.         try:
  411.             torrents = random.sample(torrents, MAX_SEED_FILES_COUNT)
  412.         except ValueError:
  413.             pass
  414.  
  415.         logger.info('we are seeding %d new torrents: %r' % (len(torrents), torrents))
  416.         for torrent in torrents:
  417.             self.add(torrent, seedOnly = True)
  418.         
  419.         logger.info('seedCompletedFiles done. In que now we have %s torrents' % len(self.store.seeding_torrents))
  420.         for i in range(len(self.store.seeding_torrents)):
  421.             if self.store.seeding_torrents[i][0]:
  422.                 logger.info('Torrent[%s]: %s' % (i, self.status(i, seedOnly = True)))
  423.                 continue
  424.         
  425.  
  426.     
  427.     def stopSeedingFiles(self):
  428.         for i in range(len(self.store.seeding_torrents)):
  429.             self.remove(i, seedOnly = True)
  430.         
  431.  
  432.     
  433.     def downloadsInProgress(self):
  434.         ''' returns array of ids
  435. \t\t'''
  436.         pass
  437.  
  438.     
  439.     def getStatAll(self):
  440.         ''' returns an array of objects returned by status '''
  441.         return len(self.store.torrents)
  442.  
  443.     
  444.     def finished(self):
  445.         '''returns array of ids
  446. \t\t'''
  447.         pass
  448.  
  449.     
  450.     def status(self, _id, seedOnly = False):
  451.         ''' returns info array for torrent of given id.
  452. \t\t'''
  453.         if seedOnly:
  454.             torrents = self.store.seeding_torrents
  455.         else:
  456.             torrents = self.store.torrents
  457.         _file = ''
  458.         totalsize = 0
  459.         gotbytes = 0
  460.         drate = 0
  461.         urate = 0
  462.         peers = 0
  463.         eta = 0
  464.         uploaded = 0
  465.         timespentsofar = 0
  466.         _spew = None
  467.         
  468.         try:
  469.             (torrent, metainfo, _file, orig) = torrents[_id]
  470.             _status = torrent.get_status(self.store.config['spew'])
  471.             totalsize = metainfo.total_bytes
  472.             gotbytes = totalsize * _status.get('fractionDone', 0)
  473.             eta = _status.get('timeEst')
  474.             if not eta:
  475.                 eta = 0
  476.             
  477.             drate = _status.get('downRate', 0)
  478.             urate = _status.get('upRate', 0)
  479.             downTotal = _status.get('downTotal', 0)
  480.             uploaded = _status.get('upTotal', 0)
  481.             peers = _status.get('numPeers', 0)
  482.             spew = _status.get('spew')
  483.             _spew = []
  484.             if spew:
  485.                 for peer in spew:
  486.                     _spew.append([
  487.                         peer['ip'],
  488.                         peer['upload'][0],
  489.                         peer['upload'][1],
  490.                         peer['download'][0],
  491.                         peer['download'][1]])
  492.                 
  493.         except Exception:
  494.             e = None
  495.             logger.warn('Exception in info: %s' % str(e))
  496.  
  497.         
  498.         try:
  499.             ret = [
  500.                 _file,
  501.                 str(int(totalsize)),
  502.                 str(int(gotbytes)),
  503.                 int(drate),
  504.                 int(urate),
  505.                 int(peers),
  506.                 int(eta),
  507.                 int(uploaded),
  508.                 _spew]
  509.         except Exception:
  510.             e = None
  511.             logger.error('status: Exception = %s' % str(e))
  512.             logger.error(str([
  513.                 _file,
  514.                 totalsize,
  515.                 gotbytes,
  516.                 drate,
  517.                 urate,
  518.                 peers,
  519.                 eta,
  520.                 uploaded,
  521.                 _spew]))
  522.             return None
  523.  
  524.         return ret
  525.  
  526.     
  527.     def shutdown(self):
  528.         ''' shutdown btdownloader
  529. \t\t\tThis function is used to shutdown the complete bittorrent core. 
  530. \t\t\tAll the torrents would be stopped.
  531. \t\t'''
  532.         
  533.         try:
  534.             logger.info('Trying to shutdown')
  535.             for _id in range(0, len(self.store.torrents)):
  536.                 self.remove(_id)
  537.             
  538.             self.dl.multitorrent.rawserver.external_add_task(self.dl.multitorrent.rawserver.doneflag.set, 0)
  539.             logger.info('Trying to shutdown2')
  540.             self.dl.coreFinished.wait(5)
  541.         except Exception:
  542.             e = None
  543.             logger.warn('Exception while shutting down: %s' % str(e))
  544.  
  545.  
  546.  
  547. if __name__ == '__main__':
  548.     logger.warn('Registering COM server...')
  549.     import win32com.server.register as win32com
  550.     win32com.server.register.UseCommandLine(BTDownloader)
  551.  
  552.