def main(self): """\ Start the Mainline client and block forever listening for connectons """ uiname = "bittorrent-console" defaults = get_defaults(uiname) config, args = configfile.parse_configuration_and_args( defaults, uiname) config = Preferences().initWithDict(config) data_dir = config['data_dir'] self.core_doneflag = DeferredEvent() self.rawserver_doneflag = DeferredEvent() rawserver = RawServer(config) #event and I/O scheduler self.multitorrent = MultiTorrent( config, rawserver, data_dir) #class used to add, control and remove torrents self.tick() #add periodic function call rawserver.add_task(0, self.core_doneflag.addCallback, lambda r: rawserver.external_add_task(0, shutdown)) rawserver.listen_forever( self.rawserver_doneflag) # runs until the component terminates self.send(producerFinished(self), "signal") print "TorrentClient has shutdown"
def main(self): """\ Start the Mainline client and block indefinitely, listening for connectons. """ uiname = "bittorrent-console" defaults = get_defaults(uiname) config, args = configfile.parse_configuration_and_args(defaults, uiname) config = Preferences().initWithDict(config) data_dir = config['data_dir'] self.core_doneflag = DeferredEvent() self.rawserver_doneflag = DeferredEvent() rawserver = RawServer(config) #event and I/O scheduler self.multitorrent = MultiTorrent(config, rawserver, data_dir) #class used to add, control and remove torrents self.tick() #add periodic function call rawserver.add_task(0, self.core_doneflag.addCallback, lambda r: rawserver.external_add_task(0, shutdown)) rawserver.listen_forever(self.rawserver_doneflag) # runs until the component terminates self.send(producerFinished(self), "signal")
class TorrentClient(threadedcomponent): """\ TorrentClient([tickInterval]) -> component capable of downloading/sharing torrents. Initialises the Mainline client. Uses threadedcomponent so it doesn't have to worry about blocking I/O or making Mainline yield periodically. Keyword arguments: - tickInterval -- the interval in seconds at which TorrentClient checks inboxes (default=5) """ Inboxes = { "inbox" : "Torrent IPC - add a torrent, stop a torrent etc.", "control" : "Shut me down" } Outboxes = { "outbox" : "Torrent IPC - status updates, completion, new torrent added etc.", "signal" : "Say when I've shutdown" } def __init__(self, tickInterval = 5): super(TorrentClient, self).__init__() self.totaltorrents = 0 self.torrents = {} self.torrentinfohashes = {} self.tickInterval = tickInterval #seconds def main(self): """\ Start the Mainline client and block indefinitely, listening for connectons. """ uiname = "bittorrent-console" defaults = get_defaults(uiname) config, args = configfile.parse_configuration_and_args(defaults, uiname) config = Preferences().initWithDict(config) data_dir = config['data_dir'] self.core_doneflag = DeferredEvent() self.rawserver_doneflag = DeferredEvent() rawserver = RawServer(config) #event and I/O scheduler self.multitorrent = MultiTorrent(config, rawserver, data_dir) #class used to add, control and remove torrents self.tick() #add periodic function call rawserver.add_task(0, self.core_doneflag.addCallback, lambda r: rawserver.external_add_task(0, shutdown)) rawserver.listen_forever(self.rawserver_doneflag) # runs until the component terminates self.send(producerFinished(self), "signal") # print ("TorrentClient has shutdown") def startTorrent(self, metainfo, save_incomplete_as, save_as, torrentid): """startTorrent causes MultiTorrent to begin downloading a torrent eventually. Use it instead of _start_torrent as it retries repeatedly if Mainline is busy.""" self._create_torrent(metainfo, save_incomplete_as, save_as) self.multitorrent.rawserver.add_task(1, self._start_torrent, metainfo, torrentid) def _create_torrent(self, metainfo, save_incomplete_as, save_as): if not self.multitorrent.torrent_known(metainfo.infohash): df = self.multitorrent.create_torrent(metainfo, save_incomplete_as, save_as) #except Exception: # e = sys.exc_info()[1] # print (e) # return False def _start_torrent(self, metainfo, torrentid): #try: t = None if self.multitorrent.torrent_known( metainfo.infohash ): t = self.multitorrent.get_torrent(metainfo.infohash) # HACK!! Rewrite when INITIALIZING state is available. if t is None or not t.is_initialized(): #self.logger.debug( "Waiting for torrent to initialize." ) self.multitorrent.rawserver.add_task(3, self._start_torrent, metainfo, torrentid) return if not self.multitorrent.torrent_running(metainfo.infohash): df = self.multitorrent.start_torrent(metainfo.infohash) self.torrents[torrentid] = self.multitorrent.get_torrent(metainfo.infohash) #yield df #df.getResult() # raises exception if one occurred in yield. # print (e) # print ("Failed to start torrent") def decodeTorrent(self, data): """\ Converts bencoded raw metadata (as one would find in a .torrent file) into a metainfo object (which one can then get the torrent's properties from). """ from BitTorrent.bencode import bdecode, bencode metainfo = None try: b = bdecode(data) metainfo = ConvertedMetainfo(b) except Exception: e = sys.exc_info()[1] pass return metainfo def handleMessages(self): while self.dataReady("inbox"): temp = self.recv("inbox") if isinstance(temp, TIPCCreateNewTorrent) or isinstance(temp, str): if isinstance(temp, str): metainfo = self.decodeTorrent(temp) else: metainfo = self.decodeTorrent(temp.rawmetainfo) if metainfo != None: savefolder = os.path.join("./",metainfo.name_fs) existingTorrentId = self.torrentinfohashes.get(metainfo.infohash, 0) if existingTorrentId != 0: self.send(TIPCTorrentAlreadyDownloading(torrentid=existingTorrentId), "outbox") else: self.totaltorrents += 1 self.torrentinfohashes[metainfo.infohash] = self.totaltorrents self.torrents[self.totaltorrents] = MakeshiftTorrent(metainfo) self.startTorrent(metainfo, savefolder, savefolder, self.totaltorrents) self.send(TIPCNewTorrentCreated(torrentid=self.totaltorrents, savefolder=savefolder), "outbox") elif isinstance(temp, TIPCCloseTorrent): try: torrent = self.torrents.get(temp.torrentid, None) self.multitorrent.remove_torrent(torrent.metainfo.infohash) self.torrentinfohashes.pop(torrent.metainfo.infohash) self.torrents.pop(temp.torrentid) except KeyError: pass while self.dataReady("control"): temp = self.recv("control") if isinstance(temp, shutdown): # print ("TorrentClient trying to shutdown") #cause us to shutdown self.rawserver_doneflag.set() self.core_doneflag.set() def sendStatusUpdates(self): "Send a TIPCTorrentStatusUpdate for each running torrent." for torrentid, torrent in self.torrents.items(): if not isinstance(torrent, MakeshiftTorrent): self.send(TIPCTorrentStatusUpdate(torrentid=torrentid, statsdictionary=torrent.get_status()), "outbox") def tick(self): """\ Called periodically... by itself (gets rawserver to call it back after a delay of tickInterval seconds). Checks inboxes and sends a status-update message for every active torrent. """ self.multitorrent.rawserver.add_task(self.tickInterval, self.tick) #print ("Tick") self.handleMessages() self.sendStatusUpdates()
def __init__(self, config, display, configfile_key): """Starts torrents for all .torrent files in a directory tree. All errors are logged using Python logging to 'configfile_key' logger. @param config: Preferences object storing config. @param display: output function for stats. """ # 4.4.x version of LaunchMany output exceptions to a displayer. # This version only outputs stats to the displayer. We do not use # the logger to output stats so that a caller-provided object # can provide stats formatting as opposed to using the # logger Formatter, which is specific to exceptions, warnings, and # info messages. self.logger = logging.getLogger(configfile_key) try: self.multitorrent = None self.rawserver = None self.config = config self.configfile_key = configfile_key self.display = display self.torrent_dir = config['torrent_dir'] # Ex: torrent_cache = infohash -> (path,metainfo) self.torrent_cache = {} # maps path -> [(modification time, size), infohash] self.file_cache = {} # used as set containing paths of files that do not have separate # entries in torrent_cache either because torrent_cache already # contains the torrent or because the torrent file is corrupt. self.blocked_files = {} #self.torrent_list = [] #self.downloads = {} self.hashcheck_queue = [] #self.hashcheck_store = {} self.hashcheck_current = None self.core_doneflag = DeferredEvent() self.rawserver = RawServer(self.config) try: # set up shut-down procedure before we begin doing things that # can throw exceptions. def shutdown(): self.logger.critical(_("shutting down")) for t in self.multitorrent.get_torrents(): self.logger.info(_('dropped "%s"') % self.torrent_cache[t.infohash][0]) if self.multitorrent: df = self.multitorrent.shutdown() set_flag = lambda *a : self.rawserver.stop() df.addCallbacks(set_flag, set_flag) else: self.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's callback and addCallback. self.core_doneflag.addCallback( lambda r: self.rawserver.external_add_task(0, shutdown)) self.rawserver.install_sigint_handler(self.core_doneflag) data_dir = config['data_dir'] self.multitorrent = MultiTorrent(config, self.rawserver, data_dir, resume_from_torrent_config=False) self.rawserver.add_task(0, self.scan) self.rawserver.add_task(0.5, self.periodic_check_hashcheck_queue) self.rawserver.add_task(self.config['display_interval'], self.periodic_stats) try: import signal def handler(signum, frame): self.rawserver.external_add_task(0, self.read_config) signal.signal(signal.SIGHUP, handler) except Exception, e: self.logger.error(_("Could not set signal handler: ") + unicode(e.args[0])) self.rawserver.add_task(0,self.core_doneflag.set()) except UserFailure, e: self.logger.error(unicode(e.args[0])) self.rawserver.add_task(0,self.core_doneflag.set()) except: data = StringIO() print_exc(file = data) self.logger.error(data.getvalue()) self.rawserver.add_task(0,self.core_doneflag.set()) # always make sure events get processed even if only for # shutting down. self.rawserver.listen_forever()
class TorrentClient(threadedcomponent): """\ TorrentClient([tickInterval]) -> component capable of downloading/sharing torrents. Initialises the Mainline client. Uses threadedcomponent so it doesn't have to worry about blocking I/O or making Mainline yield periodically. Keyword arguments: - tickInterval -- the interval in seconds at which TorrentClient checks inboxes (default=5) """ Inboxes = { "inbox": "Torrent IPC - add a torrent, stop a torrent etc.", "control": "Shut me down" } Outboxes = { "outbox": "Torrent IPC - status updates, completion, new torrent added etc.", "signal": "Say when I've shutdown" } def __init__(self, tickInterval=5): super(TorrentClient, self).__init__() self.totaltorrents = 0 self.torrents = {} self.torrentinfohashes = {} self.tickInterval = tickInterval #seconds def main(self): """\ Start the Mainline client and block indefinitely, listening for connectons. """ uiname = "bittorrent-console" defaults = get_defaults(uiname) config, args = configfile.parse_configuration_and_args( defaults, uiname) config = Preferences().initWithDict(config) data_dir = config['data_dir'] self.core_doneflag = DeferredEvent() self.rawserver_doneflag = DeferredEvent() rawserver = RawServer(config) #event and I/O scheduler self.multitorrent = MultiTorrent( config, rawserver, data_dir) #class used to add, control and remove torrents self.tick() #add periodic function call rawserver.add_task(0, self.core_doneflag.addCallback, lambda r: rawserver.external_add_task(0, shutdown)) rawserver.listen_forever( self.rawserver_doneflag) # runs until the component terminates self.send(producerFinished(self), "signal") # print ("TorrentClient has shutdown") def startTorrent(self, metainfo, save_incomplete_as, save_as, torrentid): """startTorrent causes MultiTorrent to begin downloading a torrent eventually. Use it instead of _start_torrent as it retries repeatedly if Mainline is busy.""" self._create_torrent(metainfo, save_incomplete_as, save_as) self.multitorrent.rawserver.add_task(1, self._start_torrent, metainfo, torrentid) def _create_torrent(self, metainfo, save_incomplete_as, save_as): if not self.multitorrent.torrent_known(metainfo.infohash): df = self.multitorrent.create_torrent(metainfo, save_incomplete_as, save_as) #except Exception: # e = sys.exc_info()[1] # print (e) # return False def _start_torrent(self, metainfo, torrentid): #try: t = None if self.multitorrent.torrent_known(metainfo.infohash): t = self.multitorrent.get_torrent(metainfo.infohash) # HACK!! Rewrite when INITIALIZING state is available. if t is None or not t.is_initialized(): #self.logger.debug( "Waiting for torrent to initialize." ) self.multitorrent.rawserver.add_task(3, self._start_torrent, metainfo, torrentid) return if not self.multitorrent.torrent_running(metainfo.infohash): df = self.multitorrent.start_torrent(metainfo.infohash) self.torrents[torrentid] = self.multitorrent.get_torrent( metainfo.infohash) #yield df #df.getResult() # raises exception if one occurred in yield. # print (e) # print ("Failed to start torrent") def decodeTorrent(self, data): """\ Converts bencoded raw metadata (as one would find in a .torrent file) into a metainfo object (which one can then get the torrent's properties from). """ from BitTorrent.bencode import bdecode, bencode metainfo = None try: b = bdecode(data) metainfo = ConvertedMetainfo(b) except Exception: e = sys.exc_info()[1] pass return metainfo def handleMessages(self): while self.dataReady("inbox"): temp = self.recv("inbox") if isinstance(temp, TIPCCreateNewTorrent) or isinstance(temp, str): if isinstance(temp, str): metainfo = self.decodeTorrent(temp) else: metainfo = self.decodeTorrent(temp.rawmetainfo) if metainfo != None: savefolder = os.path.join("./", metainfo.name_fs) existingTorrentId = self.torrentinfohashes.get( metainfo.infohash, 0) if existingTorrentId != 0: self.send( TIPCTorrentAlreadyDownloading( torrentid=existingTorrentId), "outbox") else: self.totaltorrents += 1 self.torrentinfohashes[ metainfo.infohash] = self.totaltorrents self.torrents[self.totaltorrents] = MakeshiftTorrent( metainfo) self.startTorrent(metainfo, savefolder, savefolder, self.totaltorrents) self.send( TIPCNewTorrentCreated(torrentid=self.totaltorrents, savefolder=savefolder), "outbox") elif isinstance(temp, TIPCCloseTorrent): try: torrent = self.torrents.get(temp.torrentid, None) self.multitorrent.remove_torrent(torrent.metainfo.infohash) self.torrentinfohashes.pop(torrent.metainfo.infohash) self.torrents.pop(temp.torrentid) except KeyError: pass while self.dataReady("control"): temp = self.recv("control") if isinstance(temp, shutdown): # print ("TorrentClient trying to shutdown") #cause us to shutdown self.rawserver_doneflag.set() self.core_doneflag.set() def sendStatusUpdates(self): "Send a TIPCTorrentStatusUpdate for each running torrent." for torrentid, torrent in self.torrents.items(): if not isinstance(torrent, MakeshiftTorrent): self.send( TIPCTorrentStatusUpdate( torrentid=torrentid, statsdictionary=torrent.get_status()), "outbox") def tick(self): """\ Called periodically... by itself (gets rawserver to call it back after a delay of tickInterval seconds). Checks inboxes and sends a status-update message for every active torrent. """ self.multitorrent.rawserver.add_task(self.tickInterval, self.tick) #print ("Tick") self.handleMessages() self.sendStatusUpdates()