bittorrent-curses.py
来自「bittorrent source by python. please enj」· Python 代码 · 共 629 行 · 第 1/2 页
PY
629 行
if self.spew_scroll_pos > len(spew): self.spew_scroll_pos = 0 for i in range(len(spew)): spew[i]['lineno'] = i+1 spew.append({'lineno': None}) spew = spew[self.spew_scroll_pos:] + spew[:self.spew_scroll_pos] for i in range(min(self.spewh - 5, len(spew))): if not spew[i]['lineno']: continue self.spewwin.addnstr(i+3, 0, '%3d' % spew[i]['lineno'], 3) self.spewwin.addnstr(i+3, 4, spew[i]['ip'], 15) ul = spew[i]['upload'] if ul[1] > 100: self.spewwin.addnstr(i+3, 20, '%6.0f KB/s' % ( ul[1] / 1000), 11) self.spewwin.addnstr(i+3, 32, '-----', 5) if ul[2]: self.spewwin.addnstr(i+3, 33, 'I', 1) if ul[3]: self.spewwin.addnstr(i+3, 35, 'C', 1) dl = spew[i]['download'] if dl[1] > 100: self.spewwin.addnstr(i+3, 38, '%6.0f KB/s' % ( dl[1] / 1000), 11) self.spewwin.addnstr(i+3, 50, '-------', 7) if dl[2]: self.spewwin.addnstr(i+3, 51, 'I', 1) if dl[3]: self.spewwin.addnstr(i+3, 53, 'C', 1) if dl[4]: self.spewwin.addnstr(i+3, 55, 'S', 1) self.spewwin.addnstr(i+3, 58, '%5.1f%%' % (int(spew[i]['completed']*1000)/10), 6) if spew[i]['speed'] is not None: self.spewwin.addnstr(i+3, 64, '%5.0f KB/s' % (spew[i]['speed']/1000), 10) """ self.spewwin.addnstr(self.spewh-1, 0, _("downloading %d pieces, have %d fragments, " "%d of %d pieces completed") % (statistics['storage_active'], statistics['storage_dirty'], statistics['storage_numcomplete'], self.numpieces), self.speww-1) """ curses.panel.update_panels() curses.doupdate()class CursesTorrentApp(object): class LogHandler(logging.Handler): def __init__(self, app, level=logging.NOTSET): logging.Handler.__init__(self,level) self.app = app def emit(self, record): if len(record.getMessage()) > 0: self.app.display_error(record.getMessage() ) if record.exc_info is not None: self.app.display_error( "Traceback (most recent call last):" ) tb = record.exc_info[2] stack = traceback.extract_tb(tb) l = traceback.format_list(stack) for s in l: self.app.display_error( " %s" % s ) self.app.display_error( " %s: %s" % ( str(record.exc_info[0]),str(record.exc_info[1]))) class LogFilter(logging.Filter): def filter( self, record): if record.name == "NatTraversal": return 0 return 1 # allow. def __init__(self, metainfo, config, errlist): assert isinstance(metainfo, ConvertedMetainfo ) self.metainfo = metainfo self.config = Preferences().initWithDict(config) self.errlist = errlist self.torrent = None self.multitorrent = None self.d = None # displayer (i.e., curses UI) self.logger = logging.getLogger("bittorrent-curses") log_handler = CursesTorrentApp.LogHandler(self) log_handler.setLevel(WARNING) #log_handler.setLevel(0) logger = logging.getLogger() logger.addHandler(log_handler) # disable stdout and stderr error reporting. global stderr_console logging.getLogger().removeHandler(console) if stderr_console is not None: logging.getLogger().removeHandler(stderr_console) # We log everything. If we want to omit certain errors then # either raise the level or install a logging.Filter: log_handler.addFilter(CursesTorrentApp.LogFilter()) logging.getLogger().setLevel(WARNING) #logging.getLogger().setLevel(0) def start_torrent(self,metainfo,save_incomplete_as,save_as): """Tells the MultiTorrent to begin downloading.""" try: self.d.display({'activity':_("initializing"), 'fractionDone':0}) multitorrent = self.multitorrent df = multitorrent.create_torrent(metainfo, save_incomplete_as, save_as) df.addErrback( wrap_log('Failed to start torrent', self.logger)) def create_finished(*args, **argv): self.torrent = multitorrent.get_torrent(metainfo.infohash) if self.torrent.is_initialized(): multitorrent.start_torrent(metainfo.infohash) else: self.d.display({'activity':_("already being downloaded"), 'fractionDone':0}) self.core_doneflag.set() # e.g., if already downloading... df.addCallback( create_finished ) except KeyboardInterrupt: raise except UserFailure, e: self.logger.error( "Failed to create torrent: " + unicode(e.args[0]) ) except Exception, e: self.logger.error( "Failed to create torrent", exc_info = e ) def run(self, scrwin): self.core_doneflag = DeferredEvent() rawserver = RawServer(self.config) # set up shut-down procedure before we begin doing things that # can throw exceptions. def shutdown(): if self.d: self.d.display({'activity':_("shutting down"), 'fractionDone':0}) if self.multitorrent: df = self.multitorrent.shutdown() set_flag = lambda *a : rawserver.stop() df.addCallbacks(set_flag, set_flag) else: rawserver.stop() # It is safe to addCallback here, because there is only one thread, # but even if the code were multi-threaded, core_doneflag has not # been passed to anyone. There is no chance of a race condition # between the DeferredEvent'scallback and addCallback. self.core_doneflag.addCallback( lambda r: rawserver.external_add_task(0, shutdown)) def reread(): if self.multitorrent is not None: rawserver.external_add_task(0,self.reread_config) def ulrate(value): if self.multitorrent is not None: self.multitorrent.set_option('max_upload_rate', value) if self.torrent != None: self.torrent.set_option('max_upload_rate', value) self.d = CursesDisplayer(scrwin, self.errlist, self.core_doneflag, reread, ulrate) rawserver.install_sigint_handler(self.core_doneflag) # semantics for --save_in vs --save_as: # save_in specifies the directory in which torrent is written. # If the torrent is a batch torrent then the files in the batch # go in save_in/metainfo.name_fs/. # save_as specifies the filename for the torrent in the case of # a non-batch torrent, and specifies the directory name # in the case of a batch torrent. Thus the files in a batch # torrent go in save_as/. metainfo = self.metainfo torrent_name = metainfo.name_fs # if batch then this contains # directory name. if config['save_as']: if config['save_in']: raise BTFailure(_("You cannot specify both --save_as and " "--save_in.")) saveas,bad = platform.encode_for_filesystem(config['save_as']) if bad: raise BTFailure(_("Invalid path encoding.")) savein = os.path.dirname(os.path.abspath(saveas)) elif config['save_in']: savein,bad = platform.encode_for_filesystem(config['save_in']) if bad: raise BTFailure(_("Invalid path encoding.")) saveas = os.path.join(savein,torrent_name) else: saveas = torrent_name if config['save_incomplete_in']: save_incomplete_in,bad = \ platform.encode_for_filesystem(config['save_incomplete_in']) if bad: raise BTFailure(_("Invalid path encoding.")) save_incomplete_as = os.path.join(save_incomplete_in,torrent_name) else: save_incomplete_as = os.path.join(savein,torrent_name) data_dir,bad = platform.encode_for_filesystem(config['data_dir']) if bad: raise BTFailure(_("Invalid path encoding.")) try: self.multitorrent = \ MultiTorrent(self.config, rawserver, data_dir, is_single_torrent = True, resume_from_torrent_config = False) self.d.set_torrent_values(metainfo.name, os.path.abspath(saveas), metainfo.total_bytes, len(metainfo.hashes)) self.start_torrent(self.metainfo, save_incomplete_as, saveas) self.get_status() except UserFailure, e: self.logger.error( unicode(e.args[0]) ) rawserver.add_task(0,core_doneflag.set()) except Exception, e: self.logger.error( "", exc_info = e ) rawserver.add_task(0,core_doneflag.set()) # always make sure events get processed even if only for # shutting down. rawserver.listen_forever() def reread_config(self): try: newvalues = configfile.get_config(self.config, 'bittorrent-curses') except Exception, e: self.d.error(_("Error reading config: ") + unicode(e.args[0])) 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) if self.torrent is not None: for option, value in newvalues.iteritems(): self.torrent.set_option(option, value) def get_status(self): self.multitorrent.rawserver.add_task(self.config['display_interval'], self.get_status) if self.torrent is not None: status = self.torrent.get_status(self.config['spew']) self.d.display(status) def display_error(self, text): """Called by the logger via LogHandler to display error messages in the curses window.""" self.d.error(text)if __name__ == '__main__': uiname = 'bittorrent-curses' defaults = get_defaults(uiname) metainfo = None if len(sys.argv) <= 1: printHelp(uiname, defaults) sys.exit(1) try: # Modifying default values from get_defaults is annoying... # Implementing specific default values for each uiname in # defaultargs.py is even more annoying. --Dave data_dir = [[name, value,doc] for (name, value, doc) in defaults if name == "data_dir"][0] defaults = [(name, value,doc) for (name, value, doc) in defaults if not name == "data_dir"] ddir = os.path.join( platform.get_dot_dir(), "curses" ) data_dir[1] = decode_from_filesystem(ddir) defaults.append( tuple(data_dir) ) config, args = configfile.parse_configuration_and_args(defaults, uiname, sys.argv[1:], 0, 1) torrentfile = None if len(args): torrentfile = args[0] if torrentfile is not None: try: metainfo = GetTorrent.get(torrentfile) except GetTorrent.GetTorrentException, e: raise UserFailure(_("Error reading .torrent file: ") + '\n' + \ unicode(e.args[0])) else: raise UserFailure(_("you must specify a .torrent file")) except BTFailure, e: print unicode(e.args[0]) sys.exit(1) errlist = [] app = CursesTorrentApp(metainfo, config, errlist) curses_wrapper(app.run) if errlist: print _("These errors occurred during execution:") for error in errlist: print error sys.stdout.flush() # if after a reasonable amount of time there are still # non-daemon threads hanging around then print them. nondaemons = [d for d in threading.enumerate() if not d.isDaemon()] if len(nondaemons) > 1: sleep(4) nondaemons = [d for d in threading.enumerate() if not d.isDaemon()] if len(nondaemons) > 1: print "non-daemon threads not shutting down:" for th in nondaemons: print " ", th
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?