From 410154779451b21ea14d7f6272c320059a772949 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Tue, 12 Aug 2008 22:00:45 -0500 Subject: Import files from BitTorrent 4.4.0 http://download.bittorrent.com/dl/BitTorrent-4.4.0.tar.gz --- bittorrent-console.py | 308 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 308 insertions(+) create mode 100755 bittorrent-console.py (limited to 'bittorrent-console.py') diff --git a/bittorrent-console.py b/bittorrent-console.py new file mode 100755 index 0000000..e5ec115 --- /dev/null +++ b/bittorrent-console.py @@ -0,0 +1,308 @@ +#!/usr/bin/env python + +# The contents of this file are subject to the BitTorrent Open Source License +# Version 1.1 (the License). You may not copy or use this file, in either +# source code or executable form, except in compliance with the License. You +# may obtain a copy of the License at http://www.bittorrent.com/license/. +# +# Software distributed under the License is distributed on an AS IS basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. + +# Written by Bram Cohen, Uoti Urpala and John Hoffman + +from __future__ import division + +from BitTorrent.platform import install_translation +install_translation() + +import sys +import os +import threading +from time import time, strftime +from cStringIO import StringIO + +from BitTorrent.download import Feedback, Multitorrent +from BitTorrent.defaultargs import get_defaults +from BitTorrent.parseargs import printHelp +from BitTorrent.zurllib import urlopen +from BitTorrent.bencode import bdecode +from BitTorrent.ConvertedMetainfo import ConvertedMetainfo +from BitTorrent.prefs import Preferences +from BitTorrent import configfile +from BitTorrent import BTFailure +from BitTorrent import version +from BitTorrent import GetTorrent + + +def fmttime(n): + if n == 0: + return _("download complete!") + try: + n = int(n) + assert n >= 0 and n < 5184000 # 60 days + except: + return _("") + m, s = divmod(n, 60) + h, m = divmod(m, 60) + return _("finishing in %d:%02d:%02d") % (h, m, s) + +def fmtsize(n): + s = str(n) + size = s[-3:] + while len(s) > 3: + s = s[:-3] + size = '%s,%s' % (s[-3:], size) + if n > 999: + unit = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'] + i = 1 + while i + 1 < len(unit) and (n >> 10) >= 999: + i += 1 + n >>= 10 + n /= (1 << 10) + size = '%s (%.0f %s)' % (size, n, unit[i]) + return size + + +class HeadlessDisplayer(object): + + def __init__(self, doneflag): + self.doneflag = doneflag + + self.done = False + self.percentDone = '' + self.timeEst = '' + self.downRate = '---' + self.upRate = '---' + self.shareRating = '' + self.seedStatus = '' + self.peerStatus = '' + self.errors = [] + self.file = '' + self.downloadTo = '' + self.fileSize = '' + self.numpieces = 0 + + def set_torrent_values(self, name, path, size, numpieces): + self.file = name + self.downloadTo = path + self.fileSize = fmtsize(size) + self.numpieces = numpieces + + def finished(self): + self.done = True + self.downRate = '---' + self.display({'activity':_("download succeeded"), 'fractionDone':1}) + + def error(self, errormsg): + newerrmsg = strftime('[%H:%M:%S] ') + errormsg + self.errors.append(newerrmsg) + self.display({}) + + def display(self, statistics): + fractionDone = statistics.get('fractionDone') + activity = statistics.get('activity') + timeEst = statistics.get('timeEst') + downRate = statistics.get('downRate') + upRate = statistics.get('upRate') + spew = statistics.get('spew') + + print '\n\n\n\n' + if spew is not None: + self.print_spew(spew) + + if timeEst is not None: + self.timeEst = fmttime(timeEst) + elif activity is not None: + self.timeEst = activity + + if fractionDone is not None: + self.percentDone = str(int(fractionDone * 1000) / 10) + if downRate is not None: + self.downRate = '%.1f KB/s' % (downRate / (1 << 10)) + if upRate is not None: + self.upRate = '%.1f KB/s' % (upRate / (1 << 10)) + downTotal = statistics.get('downTotal') + if downTotal is not None: + upTotal = statistics['upTotal'] + if downTotal <= upTotal / 100: + self.shareRating = _("oo (%.1f MB up / %.1f MB down)") % ( + upTotal / (1<<20), downTotal / (1<<20)) + else: + self.shareRating = _("%.3f (%.1f MB up / %.1f MB down)") % ( + upTotal / downTotal, upTotal / (1<<20), downTotal / (1<<20)) + numCopies = statistics['numCopies'] + nextCopies = ', '.join(["%d:%.1f%%" % (a,int(b*1000)/10) for a,b in + zip(xrange(numCopies+1, 1000), statistics['numCopyList'])]) + if not self.done: + self.seedStatus = _("%d seen now, plus %d distributed copies " + "(%s)") % (statistics['numSeeds' ], + statistics['numCopies'], + nextCopies) + else: + self.seedStatus = _("%d distributed copies (next: %s)") % ( + statistics['numCopies'], nextCopies) + self.peerStatus = _("%d seen now") % statistics['numPeers'] + + for err in self.errors[-4:]: + print _("ERROR:\n") + err + '\n' + print _("saving: "), self.file + print _("file size: "), self.fileSize + print _("percent done: "), self.percentDone + print _("time left: "), self.timeEst + print _("download to: "), self.downloadTo + print _("download rate: "), self.downRate + print _("upload rate: "), self.upRate + print _("share rating: "), self.shareRating + print _("seed status: "), self.seedStatus + print _("peer status: "), self.peerStatus + + def print_spew(self, spew): + s = StringIO() + s.write('\n\n\n') + for c in spew: + s.write('%20s ' % c['ip']) + if c['initiation'] == 'L': + s.write('l') + else: + s.write('r') + total, rate, interested, choked = c['upload'] + s.write(' %10s %10s ' % (str(int(total/10485.76)/100), + str(int(rate)))) + if c['is_optimistic_unchoke']: + s.write('*') + else: + s.write(' ') + if interested: + s.write('i') + else: + s.write(' ') + if choked: + s.write('c') + else: + s.write(' ') + + total, rate, interested, choked, snubbed = c['download'] + s.write(' %10s %10s ' % (str(int(total/10485.76)/100), + str(int(rate)))) + if interested: + s.write('i') + else: + s.write(' ') + if choked: + s.write('c') + else: + s.write(' ') + if snubbed: + s.write('s') + else: + s.write(' ') + s.write('\n') + print s.getvalue() + + +class DL(Feedback): + + def __init__(self, metainfo, config): + self.doneflag = threading.Event() + self.metainfo = metainfo + self.config = Preferences().initWithDict(config) + + def run(self): + self.d = HeadlessDisplayer(self.doneflag) + try: + self.multitorrent = Multitorrent(self.config, self.doneflag, + self.global_error) + # raises BTFailure if bad + metainfo = ConvertedMetainfo(bdecode(self.metainfo)) + torrent_name = metainfo.name_fs + if config['save_as']: + if config['save_in']: + raise BTFailure(_("You cannot specify both --save_as and " + "--save_in")) + saveas = config['save_as'] + elif config['save_in']: + saveas = os.path.join(config['save_in'], torrent_name) + else: + saveas = torrent_name + + self.d.set_torrent_values(metainfo.name, os.path.abspath(saveas), + metainfo.total_bytes, len(metainfo.hashes)) + self.torrent = self.multitorrent.start_torrent(metainfo, + Preferences(self.config), self, saveas) + except BTFailure, e: + print str(e) + return + self.get_status() + self.multitorrent.rawserver.install_sigint_handler() + self.multitorrent.rawserver.listen_forever() + self.d.display({'activity':_("shutting down"), 'fractionDone':0}) + self.torrent.shutdown() + + def reread_config(self): + try: + newvalues = configfile.get_config(self.config, 'bittorrent-console') + except Exception, e: + self.d.error(_("Error reading config: ") + str(e)) + return + self.config.update(newvalues) + # The set_option call can potentially trigger something that kills + # the torrent (when writing this the only possibility is a change in + # max_files_open causing an IOError while closing files), and so + # the self.failed() callback can run during this loop. + for option, value in newvalues.iteritems(): + self.multitorrent.set_option(option, value) + for option, value in newvalues.iteritems(): + self.torrent.set_option(option, value) + + def get_status(self): + self.multitorrent.rawserver.add_task(self.get_status, + self.config['display_interval']) + status = self.torrent.get_status(self.config['spew']) + self.d.display(status) + + def global_error(self, level, text): + self.d.error(text) + + def error(self, torrent, level, text): + self.d.error(text) + + def failed(self, torrent, is_external): + self.doneflag.set() + + def finished(self, torrent): + self.d.finished() + + +if __name__ == '__main__': + uiname = 'bittorrent-console' + defaults = get_defaults(uiname) + + metainfo = None + if len(sys.argv) <= 1: + printHelp(uiname, defaults) + sys.exit(1) + try: + config, args = configfile.parse_configuration_and_args(defaults, + uiname, sys.argv[1:], 0, 1) + + torrentfile = None + if len(args): + torrentfile = args[0] + for opt in ('responsefile', 'url'): + if config[opt]: + print '"--%s"' % opt, _("deprecated, do not use") + torrentfile = config[opt] + if torrentfile is not None: + metainfo, errors = GetTorrent.get(torrentfile) + if errors: + raise BTFailure(_("Error reading .torrent file: ") + '\n'.join(errors)) + else: + raise BTFailure(_("you must specify a .torrent file")) + except BTFailure, e: + print str(e) + sys.exit(1) + + dl = DL(metainfo, config) + dl.run() -- cgit