home *** CD-ROM | disk | FTP | other *** search
- # Source Generated with Decompyle++
- # File: in.pyc (Python 2.4)
-
- from __future__ import division
- import gettext
- gettext.install('bittorrent', 'locale')
- import sys
- import os
- import threading
- import glob
- import random
- import shutil
- from time import time, strftime
- from cStringIO import StringIO
- from encodings import *
- import urllib2
- import urlparse
- import os.path as os
- from BitTorrent.download import Feedback, Multitorrent
- from BitTorrent.defaultargs import get_defaults
- from BitTorrent.bencode import bdecode
- from BitTorrent.ConvertedMetainfo import ConvertedMetainfo
- from BitTorrent import configfile, BTFailure
- import tempfile
- dirname = tempfile.gettempdir()
- tmp_dir = dirname + '/webaroo/'
- if not os.path.isdir(tmp_dir):
- os.mkdir(tmp_dir)
-
- _logfile = tmp_dir + 'btdownloader.log'
- import logging
- import logging.handlers as logging
- logger = logging.getLogger('btdownloader')
- hdlr = logging.handlers.RotatingFileHandler(_logfile, maxBytes = 1024 * 1024, backupCount = 10)
- hdlr.setFormatter(logging.Formatter('%(asctime)s %(message)s'))
- logger.addHandler(hdlr)
- logger.setLevel(logging.INFO)
- MAX_SEED_FILES_COUNT = 4
- MAX_DOWNLOAD_FILES_COUNT = 4
-
- class DL(Feedback):
- ''' DL class
- \t\tThis class is derived from Feedback, "hosts" Multitorrent, and it runs
- \t\tin a seperate thread. Error and other messages from the multitorrent are
- \t\trecieved by respective members of this class.
- \t'''
-
- def __init__(self, store):
- logger.warn(_('Initializing DL'))
- self.store = store
- self.store.doneflag = threading.Event()
- self.multitorrent = Multitorrent(self.store.config, self.store.doneflag, self.global_error)
- self.last_error = None
- self.count_error = 0
- self.last_error_print_time = time()
-
-
- def run(self):
- logger.info('Starting Download thread.')
- self.multitorrent.rawserver.listen_forever()
- self.coreFinished.set()
- logger.info('Downloads done, shutting down bittorrent core.')
-
-
- def global_error(self, level, text):
- self._log_error(text)
-
-
- def error(self, torrent, level, text):
- self._log_error(text)
-
-
- def failed(self, torrent, is_external):
- logger.error('Failed!!!')
-
-
- def finished(self, torrent):
- logger.warn('Finished')
-
-
- def _log_error(self, msg):
- if msg == self.last_error:
- self.count_error += 1
- if time() - self.last_error_print_time >= 5 and self.count_error:
- logger.error('Last error repeated %d times.' % self.count_error)
- self.last_error_print_time = time()
-
- elif self.count_error:
- logger.error('Error repeated %d times: %s' % (self.count_error, self.last_error))
-
- logger.error('Error: %s' % msg)
- self.last_error = msg
- self.count_error = 0
- self.last_error_print_time = time()
-
-
-
- class DLThread(threading.Thread):
- ''' DLThread class
- \t\tThis class is instantiated once in the __init__ of BTDownloader, used
- \t\tto run a thread for downloading.
- \t'''
-
- def __init__(self):
- threading.Thread.__init__(self)
- logger.warn('Initializing thread.')
-
-
- def run(self):
- logger.warn('DLThread.run called.')
- self.dl.run()
-
-
-
- class Store:
- ''' Store class.
- \t\tMost of the data used in this module are stored in this store.
-
- \t\tMain elements are:
- \t\tself.torrents: a list containg tuple of torrent, metainfo and file name
- \t\t being downloaded.
- \t\tself.dl: an instance of DL.
- \t\tself.dlthread: an instance of DLThread.
- \t'''
-
- def __init__(self):
- self.torrents = []
- self.seeding_torrents = []
- self.uiname = 'btdownloadcurses'
- self.defaults = get_defaults(self.uiname)
-
- try:
- (self.config, args) = configfile.parse_configuration_and_args(self.defaults, self.uiname, [], 0, 1, tmp_dir)
- except BTFailure:
- e = None
- logger.error('Parsing arguments failed(%s). Bailing out.' % str(e))
- sys.exit(1)
-
-
-
-
- class BTDownloader:
- ''' class BTDownloader
- \t\tThis is the main class, can be registered as a COM server or can be
- \t\tused as standard python module.
-
- \t\tCreating an instance of this class starts a bittorrent core in a
- \t\tseperate thread. No clean up is required at the end, it will
- \t\tautomatically shutdown everything cleanly as far as possible when
- \t\tthe reference is freed up.
-
- \t\tMost of the bittorrent related configuration and data are stored in a
- \t\tclass Store instance self.store.\t\t
- \t'''
- _public_methods_ = [
- 'getStatAll',
- 'setDataDir',
- 'add',
- 'remove',
- 'downloadsInProgress',
- 'finished',
- 'status',
- 'shutdown',
- 'seedCompletedFiles',
- 'stopSeedingFiles',
- 'remove_file']
- _reg_progid_ = 'Webaroo.BTDownloader'
- _reg_clsid_ = '{DB5B62B0-57BD-4915-8932-99CD5BEABFA2}'
-
- def __init__(self, rerequest_interval = 100, min_peers = 50, max_upload_rate = 0):
- logger.warn('initializing com server')
-
- def handle_errors(f, p, e):
- logger.error('Error while removing .bittorrent: %s, %s, %s' % (f, p, e))
-
- shutil.rmtree(os.path.join(tmp_dir, '.bittorrent'), onerror = handle_errors)
- self.store = Store()
- self.store.config['rerequest_interval'] = rerequest_interval
- self.store.config['min_peers'] = min_peers
- self.store.config['max_upload_rate'] = max_upload_rate
- self.store.config['spew'] = True
- self.store.config['timeout_check_interval'] = 5.0
- self.store.config['fast_socket_timeout'] = 5.0
- self.store.config['socket_timeout'] = 60.0
- self.dl = DL(self.store)
- self.dl.coreFinished = threading.Event()
- self.dlthread = DLThread()
- self.dlthread.dl = self.dl
- self.dlthread.start()
-
-
- def setDataDir(self, datadir):
- logger.info('setDataDir: called with %s' % datadir)
- self.datadir = datadir
-
-
- def __del__(self):
- logger.warn('Shutting down COM server.')
- self.shutdown()
-
-
- def getMetaInfo(self, url, location = None):
- if location == None:
- location = self.datadir
-
- tdata = ''
- orig = ''
-
- try:
- h = file(url, 'rb')
- tdata = h.read()
- h.close()
- file_name = os.path.split(url)[1]
- orig = file_name
- except:
-
- try:
- tdata = urllib2.urlopen(url).read()
- except Exception:
- e = None
- logger.error('Cant read file: %s[%s]' % (url, str(e)))
-
- file_name = os.path.split(urlparse.urlparse(url)[2])[1]
- orig = file_name
-
- try:
- file(os.path.join(location, file_name), 'wb').write(tdata)
-
-
-
- try:
- metainfo = ConvertedMetainfo(bdecode(tdata))
- except BTFailure:
- logger.info('Invalid torrent file(%s).' % url)
- return (None, None)
-
- return (metainfo, orig)
-
-
- def cleanupTorrents(self, seedOnly = False):
- logger.info('cleanupTorrents called; seedOnly: %s' % seedOnly)
- if seedOnly:
- torrents = self.store.seeding_torrents
- MAX_FILES_COUNT = MAX_SEED_FILES_COUNT
- else:
- torrents = self.store.torrents
- MAX_FILES_COUNT = MAX_DOWNLOAD_FILES_COUNT
- ntorrents = [](_[1])
- for i in range(ntorrents):
- stat = self.status(i)
- if stat[1] == stat[2]:
- logger.info('cleanupTorrents: removing complete file %s' % stat[3])
- self.remove(i, seedOnly)
- return True
- continue
- None if ntorrents < MAX_FILES_COUNT else len
-
- return False
-
-
- def isSeeded(self, orig, seedOnly = False):
- if seedOnly:
- torrents = self.store.seeding_torrents
- else:
- torrents = self.store.torrents
- for i in range(len(torrents)):
- if torrents[i][3] == orig:
- if torrents[i][0]:
- if os.path.exists(torrents[i][2]):
- logger.warn('File %s already seeded and available. Returning id: %s;seedOnly: %s' % (orig, i, seedOnly))
- return i
- else:
- self.remove(i, seedOnly)
- return -1
- else:
- logger.warn('THIS SHOULD NEVER HAPPEN')
- return -1
- torrents[i][0]
-
- return -1
-
-
- def add(self, url, location = None, seedOnly = False):
- ''' add torrent.
- \t\t\tThis function adds the torrent file specified in url to the
- \t\t\tbittorrent core, and starts downloading it. If location is supplied
- \t\t\tit will save the file in location directory.
- \t\t\t
- \t\t\tTo get status of download call status.
- \t\t'''
- logger.info('get called with: url = %s, location = %s' % (url, location))
- if location is None:
- location = self.datadir
-
- (metainfo, orig) = self.getMetaInfo(url, location)
- if not metainfo:
- return -1
-
- index = self.isSeeded(orig, seedOnly = True)
- if index >= 0:
- if not seedOnly:
- self.torrents.append(self.seeding_torrents[index])
- self.seeding_torrents[index] = [
- None,
- None,
- None,
- None]
- return len(self.torrents) - 1
- else:
- return index
-
- index = self.isSeeded(orig)
- if index >= 0:
- logger.info('Received index for: url = %s, with id = %s' % (url, index))
- return index
-
- if not self.cleanupTorrents(seedOnly):
- logger.warn("cleanupTorrents returned false, can't que more torrents for now.")
- return -1
-
- save_as = os.path.join(location, metainfo.name_fs)
-
- try:
- logger.info('add: calling start_torrent with %s' % save_as)
- torrent = self.dl.multitorrent.start_torrent(metainfo, self.store.config, self.dl, save_as)
- except Exception:
- e = None
- logger.warn('Exception while starting torrent: %s' % str(e))
- return -1
-
- if seedOnly:
- self.store.seeding_torrents.append((torrent, metainfo, save_as, orig))
- id = len(self.store.seeding_torrents) - 1
- else:
- self.store.torrents.append((torrent, metainfo, save_as, orig))
- id = len(self.store.torrents) - 1
- logger.info('add: returning with %s for %s[seedOnly: %s]' % (url, id, seedOnly))
- return id
-
-
- def remove(self, id, seedOnly = False):
- if seedOnly:
- torrents = self.store.seeding_torrents
- else:
- torrents = self.store.torrents
-
- try:
- (torrent, meta, _file, orig) = torrents[id]
- logger.warn('Shutting down %s, status: %s' % (orig, str(self.status(id))))
- if torrent:
- torrent.shutdown()
-
- torrents[id] = (None, None, None, None)
- except Exception:
- e = None
- logger.warn('Exception during shutting down torrent[%s]: %s' % (_file, str(e)))
-
-
-
- def isDownloadDone(self, torrent_file):
- if not torrent_file.lower().endswith('.torrent'):
- return False
-
- (metainfo, localname) = self.getMetaInfo(torrent_file)
- if not metainfo:
- return False
-
- local_filename = torrent_file[:-len('.torrent')]
- size_as_per_torrent = metainfo.total_bytes
- if os.path.exists(local_filename):
- size_on_disk = os.path.getsize(local_filename)
- else:
- size_on_disk = 0
- logger.info('isDownloadDone: [%s] on disk = %s; in metainfo = %s' % (torrent_file, size_on_disk, size_as_per_torrent))
- return size_as_per_torrent == size_on_disk
-
-
- def remove_file(self, name):
- for i in range(len(self.store.seeding_torrents)):
- metainfo = self.store.seeding_torrents[i][1]
- if metainfo:
- if metainfo.name_fs == name:
- logger.info('remove_file: removing file %s' % name)
- self.remove(i, seedOnly = True)
- return None
-
- metainfo.name_fs == name
-
- for i in range(len(self.store.torrents)):
- metainfo = self.store.torrents[i][1]
- if metainfo:
- if metainfo.name_fs == name:
- logger.info('remove_file: removing file %s' % name)
- self.remove(i)
- return None
-
- metainfo.name_fs == name
-
- logger.info('remove_file: did not find any instace of the file')
-
-
- def seedCompletedFiles(self):
- '''Start seeding files already present in the given directory.'''
- logger.info('seedCompletedFiles called, datadir: %s' % self.datadir)
- torrents = glob.glob(os.path.join(self.datadir, '*.torrent'))
- logger.info('Torrents in cache directory are: %r' % torrents)
- torrents = filter(self.isDownloadDone, torrents)
- logger.info('Finished torrents in cache directory are: %r' % torrents)
-
- try:
- torrents = random.sample(torrents, MAX_SEED_FILES_COUNT)
- except ValueError:
- pass
-
- logger.info('we are seeding %d new torrents: %r' % (len(torrents), torrents))
- for torrent in torrents:
- self.add(torrent, seedOnly = True)
-
- logger.info('seedCompletedFiles done. In que now we have %s torrents' % len(self.store.seeding_torrents))
- for i in range(len(self.store.seeding_torrents)):
- if self.store.seeding_torrents[i][0]:
- logger.info('Torrent[%s]: %s' % (i, self.status(i, seedOnly = True)))
- continue
-
-
-
- def stopSeedingFiles(self):
- for i in range(len(self.store.seeding_torrents)):
- self.remove(i, seedOnly = True)
-
-
-
- def downloadsInProgress(self):
- ''' returns array of ids
- \t\t'''
- pass
-
-
- def getStatAll(self):
- ''' returns an array of objects returned by status '''
- return len(self.store.torrents)
-
-
- def finished(self):
- '''returns array of ids
- \t\t'''
- pass
-
-
- def status(self, _id, seedOnly = False):
- ''' returns info array for torrent of given id.
- \t\t'''
- if seedOnly:
- torrents = self.store.seeding_torrents
- else:
- torrents = self.store.torrents
- _file = ''
- totalsize = 0
- gotbytes = 0
- drate = 0
- urate = 0
- peers = 0
- eta = 0
- uploaded = 0
- timespentsofar = 0
- _spew = None
-
- try:
- (torrent, metainfo, _file, orig) = torrents[_id]
- _status = torrent.get_status(self.store.config['spew'])
- totalsize = metainfo.total_bytes
- gotbytes = totalsize * _status.get('fractionDone', 0)
- eta = _status.get('timeEst')
- if not eta:
- eta = 0
-
- drate = _status.get('downRate', 0)
- urate = _status.get('upRate', 0)
- downTotal = _status.get('downTotal', 0)
- uploaded = _status.get('upTotal', 0)
- peers = _status.get('numPeers', 0)
- spew = _status.get('spew')
- _spew = []
- if spew:
- for peer in spew:
- _spew.append([
- peer['ip'],
- peer['upload'][0],
- peer['upload'][1],
- peer['download'][0],
- peer['download'][1]])
-
- except Exception:
- e = None
- logger.warn('Exception in info: %s' % str(e))
-
-
- try:
- ret = [
- _file,
- str(int(totalsize)),
- str(int(gotbytes)),
- int(drate),
- int(urate),
- int(peers),
- int(eta),
- int(uploaded),
- _spew]
- except Exception:
- e = None
- logger.error('status: Exception = %s' % str(e))
- logger.error(str([
- _file,
- totalsize,
- gotbytes,
- drate,
- urate,
- peers,
- eta,
- uploaded,
- _spew]))
- return None
-
- return ret
-
-
- def shutdown(self):
- ''' shutdown btdownloader
- \t\t\tThis function is used to shutdown the complete bittorrent core.
- \t\t\tAll the torrents would be stopped.
- \t\t'''
-
- try:
- logger.info('Trying to shutdown')
- for _id in range(0, len(self.store.torrents)):
- self.remove(_id)
-
- self.dl.multitorrent.rawserver.external_add_task(self.dl.multitorrent.rawserver.doneflag.set, 0)
- logger.info('Trying to shutdown2')
- self.dl.coreFinished.wait(5)
- except Exception:
- e = None
- logger.warn('Exception while shutting down: %s' % str(e))
-
-
-
- if __name__ == '__main__':
- logger.warn('Registering COM server...')
- import win32com.server.register as win32com
- win32com.server.register.UseCommandLine(BTDownloader)
-
-