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 listen_forever(self): pool = NSAutoreleasePool.alloc().init() # XXX #self.profile = Profile("BT.prof");self.profile.start() self.rawserver = RawServer(self.config) self.mt = MultiTorrent(self.config, self.doneflag, self.rawserver, self.multi_errorfunc, self.config['data_dir']) self.rawserver.ident = thread.get_ident() self.mt.set_option("max_upload_rate", self.config['max_upload_rate'] * 1024) self.rawserver.listen_forever(self.doneflag)
def init_core(mainloop): core_doneflag = DeferredEvent() class UILogger(logging.Handler): def emit(self, record): msg = "[%s] %s" % (record.name, self.format(record)) gui_wrap(mainloop.do_log, record.levelno, msg) logging.getLogger('').addHandler(UILogger()) try: multitorrent = MultiTorrent(config, rawserver, config['data_dir'], listen_fail_ok=True, init_torrents=False) # Butlers multitorrent.add_policy(DownloadTorrentButler(multitorrent)) multitorrent.add_policy(SeedTorrentButler(multitorrent)) auto_update_butler = AutoUpdateButler(multitorrent, rawserver, test_new_version=config['new_version'], test_current_version=config['current_version']) multitorrent.add_auto_update_policy(auto_update_butler) # attach to the UI tpm = ThreadProxy(multitorrent, gui_wrap, wrap_task(rawserver.external_add_task)) mainloop.attach_multitorrent(tpm, core_doneflag) ipc.start(mainloop.external_command) #rawserver.associate_thread() # register shutdown action def shutdown(): df = multitorrent.shutdown() stop_rawserver = lambda r : rawserver.stop() df.addCallbacks(stop_rawserver, stop_rawserver) rawserver.add_task(0, core_doneflag.addCallback, lambda r: rawserver.external_add_task(0, shutdown)) rawserver.listen_forever() except: # oops, we failed. # one message for the log w/ exception info global_logger.exception("BitTorrent core initialization failed!") # one message for the user w/o info global_logger.critical("BitTorrent core initialization failed!") core_doneflag.set() rawserver.stop() try: gui_wrap(mainloop.ExitMainLoop) except: pass try: gui_wrap(mainloop.doneflag.set) except: pass raise
def create_multitorrent(config, rawserver, i): config = Preferences().initWithDict(dict(config)) config['data_dir'] = config['data_dir'] + ("_%s" % i) if os.path.exists(config['data_dir']): shutil.rmtree(config['data_dir']) for d in ('', 'resume', 'metainfo', 'torrents'): ddir = os.path.join(config['data_dir'], d) no_really_makedirs(ddir) multitorrent = MultiTorrent(config, rawserver, config['data_dir'], init_torrents=False) # Butlers multitorrent.add_policy(DownloadTorrentButler(multitorrent)) multitorrent.add_policy(SeedTorrentButler(multitorrent)) # register shutdown action def shutdown(): df = multitorrent.shutdown() stop_rawserver = lambda r: rawserver.stop() df.addCallbacks(stop_rawserver, stop_rawserver) rawserver.add_task(0, core_doneflag.addCallback, lambda r: rawserver.external_add_task(0, shutdown)) return multitorrent
def create_multitorrent(config, rawserver, i): config = Preferences().initWithDict(dict(config)) config['data_dir'] = config['data_dir'] + ("_%s" % i) if os.path.exists(config['data_dir']): shutil.rmtree(config['data_dir']) for d in ('', 'resume', 'metainfo', 'torrents'): ddir = os.path.join(config['data_dir'], d) no_really_makedirs(ddir) multitorrent = MultiTorrent(config, rawserver, config['data_dir'], init_torrents=False) # Butlers multitorrent.add_policy(DownloadTorrentButler(multitorrent)) multitorrent.add_policy(SeedTorrentButler(multitorrent)) # register shutdown action def shutdown(): df = multitorrent.shutdown() stop_rawserver = lambda r : rawserver.stop() df.addCallbacks(stop_rawserver, stop_rawserver) rawserver.add_task(0, core_doneflag.addCallback, lambda r: rawserver.external_add_task(0, shutdown)) return multitorrent
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. Arguments: - [tickInterval=5] -- the interval in seconds at which TorrentClient checks inboxes Uses threadedcomponent so it doesn't have to worry about blocking I/O or making Mainline yield periodically. """ 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: # 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: pass return metainfo
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")) if self.multitorrent: if len(self.multitorrent.get_torrents()) > 0: for t in self.multitorrent.get_torrents(): self.logger.info( _('dropped "%s"') % self.torrent_cache[t.infohash][0]) def after_mt(r): self.logger.critical( "multitorrent shutdown completed. Calling rawserver.stop" ) self.rawserver.stop() self.logger.critical("calling multitorrent shutdown") df = self.multitorrent.shutdown() #set_flag = lambda *a : self.rawserver.stop() df.addCallbacks(after_mt, after_mt) else: self.rawserver.stop() ### PROFILER POSTPROCESSING. #self.logger.critical( "Disabling profiles" ) #prof.disable() #self.logger.critical( "Running profiler post-processing" ) #stats = Stats(prof.getstats()) #stats.sort("inlinetime") #self.logger.info( "Calling stats.pprint") #stats.pprint() #self.logger.info( "After stats.pprint") ### PROFILER POSTPROCESSING # 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) # first time sleep_time is 0 so that it will startup fast. On # subsequent scans, the scan is slowed down to not affect performance. self.rawserver.add_task(0, self.scan, sleep_time=0) 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) if hasattr(signal, 'SIGHUP'): signal.signal(signal.SIGHUP, handler) except Exception, e: self.logger.error( _("Could not set signal handler: ") + str_exc(e)) self.rawserver.add_task(0, self.core_doneflag.set) except UserFailure, e: self.logger.error(str_exc(e)) self.rawserver.add_task(0, self.core_doneflag.set) except: #data = StringIO() #print_exc(file = data) #self.logger.error(data.getvalue()) self.logger.exception( "Exception raised while initializing LaunchMany") 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() self.logger.info("After 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()
class BTAppController (NibClassBuilder.AutoBaseClass): def init(self): self = super(BTAppController, self).init() self.prefs = Preferences.alloc().init() self.prefwindow = None self.generator = Generate.alloc().init() self.ic =ICHelper.alloc().init() # displayed torrent controllers self.torrents = [] # waiting to die self.dead_torrents = [] # ready to start # (<open panel>, <insert row>, (<filename>|<stream>, <is_stream>)) -1 insert row means use last row # stream = 0 if filename, 1 if bencoded torrent file self.tqueue = [] self.retain() self.inited = 0 self.launched = 0 self.in_choose = 0 self.last_qcheck = time() self.sc = 0 self.defaults = NSUserDefaults.standardUserDefaults() self.tup = bdecode(self.defaults.objectForKey_(ULBYTES)) self.tdown = bdecode(self.defaults.objectForKey_(DLBYTES)) self.config = common_options + rare_options self.config = BTPreferences().initWithDict(dict([(name, value) for (name, value, doc) in self.config])) self.config['data_dir'] = PREFDIR self.config['bind'] = '' self.config['bad_libc_workaround'] = True self.config['filesystem_encoding'] = 'utf8' #XXXX #self.config['start_trackerless_client'] = False self.legacyConfig() self.reloadConfig() self.pyi = None self.doneflag = Event() if not os.path.exists(PREFDIR): os.mkdir(PREFDIR) if not os.path.exists(RESDIR): os.mkdir(RESDIR) self.ticon = None self.stalled = [] self.terminated = False return self def loadConsole_(self, sender): if not self.pyi: self.pyi = PyInterpreter.alloc().init() NSBundle.loadNibNamed_owner_("PyInterpreter", self.pyi) else: self.pyi.textView.window().makeKeyAndOrderFront_(self) def legacyConfig(self): n = self.defaults.integerForKey_(QUEUESTOP) if n > 1: self.defaults.setObject_forKey_(0, QUEUESTOP) def reloadConfig(self): self.config['minport'] = self.defaults.integerForKey_(MINPORT) self.config['maxport'] = self.defaults.integerForKey_(MINPORT) self.config['max_upload_rate'] = self.defaults.integerForKey_(MAXULRATE) self.config['max_allow_in'] = self.defaults.integerForKey_(MAXACCEPT) self.config['max_initiate'] = self.defaults.integerForKey_(MAXINITIATE) self.config['max_uploads'] = self.defaults.integerForKey_(MAXULS) def listen_forever(self): pool = NSAutoreleasePool.alloc().init() # XXX #self.profile = Profile("BT.prof");self.profile.start() self.rawserver = RawServer(self.config) self.mt = MultiTorrent(self.config, self.doneflag, self.rawserver, self.multi_errorfunc, self.config['data_dir']) self.rawserver.ident = thread.get_ident() self.mt.set_option("max_upload_rate", self.config['max_upload_rate'] * 1024) self.rawserver.listen_forever(self.doneflag) #self.profile.stop();self.profile.close() def multi_errorfunc(self, level, text ): if level == CRITICAL: self.statusField.setStringValue_(NSLocalizedString("Critical Error: %s", "critical error") % text) # bomb out elif level == ERROR: self.statusField.setStringValue_(NSLocalizedString("Error: %s", "normal error") % text) elif level == WARNING: print ">>>", NSLocalizedString("Warning: %s", "warning error") % text elif level == INFO: print ">>>", NSLocalizedString("Info: %s", "info error") % text def awakeFromNib(self): if not self.inited: self.inited = 1 NSBundle.loadNibNamed_owner_("TorrentWindow", self) self.drawerMenu.setTarget_(self.logDrawer) self.drawerMenu.setAction_(self.logDrawer.toggle_) self.logDrawer.delegate().menu = self.drawerMenu self.torrentTable.registerForDraggedTypes_([NSFilenamesPboardType]) def openPage_(self, sender): NSWorkspace.sharedWorkspace().openURL_(NSURL.URLWithString_(URL)) def openHomePage_(self, sender): NSWorkspace.sharedWorkspace().openURL_(NSURL.URLWithString_("http://www.bittorrent.com/")) def nag(self): nag = self.defaults.objectForKey_(NAG) if nag == 0 or (nag != BitTorrent.version and randint(0,2) == 0): if nag == 0: self.defaults.setObject_forKey_(1, NAG) NSWorkspace.sharedWorkspace().openURL_(NSURL.URLWithString_(URL)) x = NSRunAlertPanel(NSLocalizedString("Please Donate", "nag window title"), NSLocalizedString("If you like BitTorrent and want to see more cool apps from the author, you should make a donation.", "nag message"), NSLocalizedString("Later", "nag later"), NSLocalizedString("Already Donated", "nag already"), None) if x == NSAlertAlternateReturn: self.defaults.setObject_forKey_(BitTorrent.version, NAG) NSRunInformationalAlertPanel(NSLocalizedString("Thank You", "Thank You"), NSLocalizedString("Thank you for making a donation. You will not be bothered with donation requests until you upgrade.", "thanks for donating"), NSLocalizedString("OK", "OK"), None, None) def applicationDidFinishLaunching_(self, aNotification): try: NSThread.detachNewThreadSelector_toTarget_withObject_(self.listen_forever, self, None) except BTFailure, e: err = str(e) if err.startswith("Could not open a listening port"): p = PortChanger.alloc().initWithErr(err) else: x = NSRunAlertPanel(NSLocalizedString("Fatal Error", "fatal error"), NSLocalizedString("Failed to initialize BitTorrent core. Error: %s", "bittorrent failure message") % str(e), None, None, None) NSApp().terminate_(self) self.launched = 1 #self.profile = Profile("BT.prof") #self.profile.start() tcell = PyTimeCell.alloc().init() fcell = PyFileCell.alloc().init() xcell = PyXFerCell.alloc().init() cols = self.torrentTable.tableColumns() for c in cols: #c.setHeaderCell_(TorrentTableHeaderCell.alloc().initTextCell_(c.headerCell().stringValue())) if c.identifier() == 'time': c.setDataCell_(tcell) elif c.identifier() == 'file': c.setDataCell_(fcell) elif c.identifier() == 'xfer': c.setDataCell_(xcell) NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(self, self._otorrent, "DoneChoosingTorrent", None) NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(self, self.saveTorrents, "NSWorkspaceWillPowerOffNotification", None) NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(self, self.moveTorrent, "NSTableViewColumnDidMoveNotification", None) NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(1.5, self, self.updateStatus, None, 1) NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(10, self, self.checkQueue, None, 1) NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(300, self, self.saveTorrents, None, 1) self.loadTorrents() NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(self, self.reapDead, "TorrentStatusChanged", None) NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(self, self.statusChanged, "TorrentStatusChanged", None) NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(self, self.updateCycleMenu, "TorrentStatusChanged", None) NSNotificationCenter.defaultCenter().addObserver_selector_name_object_(self, self.updateCycleMenu, "TorrentSelectionChanged", None) if defaults.objectForKey_(VERSIONCHECK): self.checkVersion() # open any torrents waiting self.otorrent(None)
class TorrentClient(threadedcomponent): """\ TorrentClient([tickInterval]) -> component capable of downloading/sharing torrents. Initialises the Mainline client. Arguments: - [tickInterval=5] -- the interval in seconds at which TorrentClient checks inboxes Using threadedcomponent so we don't have to worry about blocking I/O or making mainline yield periodically """ Inboxes = { "inbox": "Torrent IPC - add a torrent, stop a torrent etc.", "control": "NOT USED" } Outboxes = { "outbox": "Torrent IPC - status updates, completion, new torrent added etc.", "signal": "NOT USED" } 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 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 startTorrent(self, metainfo, save_incomplete_as, save_as, torrentid): """startTorrent causes MultiTorrent to begin downloading a torrent eventually. Use it instead of _start_torrent.""" 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: # 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: pass return metainfo
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()
class BTAppController(NibClassBuilder.AutoBaseClass): def init(self): self = super(BTAppController, self).init() self.prefs = Preferences.alloc().init() self.prefwindow = None self.generator = Generate.alloc().init() self.ic = ICHelper.alloc().init() # displayed torrent controllers self.torrents = [] # waiting to die self.dead_torrents = [] # ready to start # (<open panel>, <insert row>, (<filename>|<stream>, <is_stream>)) -1 insert row means use last row # stream = 0 if filename, 1 if bencoded torrent file self.tqueue = [] self.retain() self.inited = 0 self.launched = 0 self.in_choose = 0 self.last_qcheck = time() self.sc = 0 self.defaults = NSUserDefaults.standardUserDefaults() self.tup = bdecode(self.defaults.objectForKey_(ULBYTES)) self.tdown = bdecode(self.defaults.objectForKey_(DLBYTES)) self.config = common_options + rare_options self.config = BTPreferences().initWithDict( dict([(name, value) for (name, value, doc) in self.config])) self.config['data_dir'] = PREFDIR self.config['bind'] = '' self.config['bad_libc_workaround'] = True self.config['filesystem_encoding'] = 'utf8' #XXXX #self.config['start_trackerless_client'] = False self.legacyConfig() self.reloadConfig() self.pyi = None self.doneflag = Event() if not os.path.exists(PREFDIR): os.mkdir(PREFDIR) if not os.path.exists(RESDIR): os.mkdir(RESDIR) self.ticon = None self.stalled = [] self.terminated = False return self def loadConsole_(self, sender): if not self.pyi: self.pyi = PyInterpreter.alloc().init() NSBundle.loadNibNamed_owner_("PyInterpreter", self.pyi) else: self.pyi.textView.window().makeKeyAndOrderFront_(self) def legacyConfig(self): n = self.defaults.integerForKey_(QUEUESTOP) if n > 1: self.defaults.setObject_forKey_(0, QUEUESTOP) def reloadConfig(self): self.config['minport'] = self.defaults.integerForKey_(MINPORT) self.config['maxport'] = self.defaults.integerForKey_(MINPORT) self.config['max_upload_rate'] = self.defaults.integerForKey_( MAXULRATE) self.config['max_allow_in'] = self.defaults.integerForKey_(MAXACCEPT) self.config['max_initiate'] = self.defaults.integerForKey_(MAXINITIATE) self.config['max_uploads'] = self.defaults.integerForKey_(MAXULS) def listen_forever(self): pool = NSAutoreleasePool.alloc().init() # XXX #self.profile = Profile("BT.prof");self.profile.start() self.rawserver = RawServer(self.config) self.mt = MultiTorrent(self.config, self.doneflag, self.rawserver, self.multi_errorfunc, self.config['data_dir']) self.rawserver.ident = thread.get_ident() self.mt.set_option("max_upload_rate", self.config['max_upload_rate'] * 1024) self.rawserver.listen_forever(self.doneflag) #self.profile.stop();self.profile.close() def multi_errorfunc(self, level, text): if level == CRITICAL: self.statusField.setStringValue_( NSLocalizedString("Critical Error: %s", "critical error") % text) # bomb out elif level == ERROR: self.statusField.setStringValue_( NSLocalizedString("Error: %s", "normal error") % text) elif level == WARNING: print ">>>", NSLocalizedString("Warning: %s", "warning error") % text elif level == INFO: print ">>>", NSLocalizedString("Info: %s", "info error") % text def awakeFromNib(self): if not self.inited: self.inited = 1 NSBundle.loadNibNamed_owner_("TorrentWindow", self) self.drawerMenu.setTarget_(self.logDrawer) self.drawerMenu.setAction_(self.logDrawer.toggle_) self.logDrawer.delegate().menu = self.drawerMenu self.torrentTable.registerForDraggedTypes_([NSFilenamesPboardType]) def openPage_(self, sender): NSWorkspace.sharedWorkspace().openURL_(NSURL.URLWithString_(URL)) def openHomePage_(self, sender): NSWorkspace.sharedWorkspace().openURL_( NSURL.URLWithString_("http://www.bittorrent.com/")) def nag(self): nag = self.defaults.objectForKey_(NAG) if nag == 0 or (nag != BitTorrent.version and randint(0, 2) == 0): if nag == 0: self.defaults.setObject_forKey_(1, NAG) NSWorkspace.sharedWorkspace().openURL_(NSURL.URLWithString_(URL)) x = NSRunAlertPanel( NSLocalizedString("Please Donate", "nag window title"), NSLocalizedString( "If you like BitTorrent and want to see more cool apps from the author, you should make a donation.", "nag message"), NSLocalizedString("Later", "nag later"), NSLocalizedString("Already Donated", "nag already"), None) if x == NSAlertAlternateReturn: self.defaults.setObject_forKey_(BitTorrent.version, NAG) NSRunInformationalAlertPanel( NSLocalizedString("Thank You", "Thank You"), NSLocalizedString( "Thank you for making a donation. You will not be bothered with donation requests until you upgrade.", "thanks for donating"), NSLocalizedString("OK", "OK"), None, None) def applicationDidFinishLaunching_(self, aNotification): try: NSThread.detachNewThreadSelector_toTarget_withObject_( self.listen_forever, self, None) except BTFailure, e: err = str(e) if err.startswith("Could not open a listening port"): p = PortChanger.alloc().initWithErr(err) else: x = NSRunAlertPanel( NSLocalizedString("Fatal Error", "fatal error"), NSLocalizedString( "Failed to initialize BitTorrent core. Error: %s", "bittorrent failure message") % str(e), None, None, None) NSApp().terminate_(self) self.launched = 1 #self.profile = Profile("BT.prof") #self.profile.start() tcell = PyTimeCell.alloc().init() fcell = PyFileCell.alloc().init() xcell = PyXFerCell.alloc().init() cols = self.torrentTable.tableColumns() for c in cols: #c.setHeaderCell_(TorrentTableHeaderCell.alloc().initTextCell_(c.headerCell().stringValue())) if c.identifier() == 'time': c.setDataCell_(tcell) elif c.identifier() == 'file': c.setDataCell_(fcell) elif c.identifier() == 'xfer': c.setDataCell_(xcell) NSNotificationCenter.defaultCenter().addObserver_selector_name_object_( self, self._otorrent, "DoneChoosingTorrent", None) NSNotificationCenter.defaultCenter().addObserver_selector_name_object_( self, self.saveTorrents, "NSWorkspaceWillPowerOffNotification", None) NSNotificationCenter.defaultCenter().addObserver_selector_name_object_( self, self.moveTorrent, "NSTableViewColumnDidMoveNotification", None) NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_( 1.5, self, self.updateStatus, None, 1) NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_( 10, self, self.checkQueue, None, 1) NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_( 300, self, self.saveTorrents, None, 1) self.loadTorrents() NSNotificationCenter.defaultCenter().addObserver_selector_name_object_( self, self.reapDead, "TorrentStatusChanged", None) NSNotificationCenter.defaultCenter().addObserver_selector_name_object_( self, self.statusChanged, "TorrentStatusChanged", None) NSNotificationCenter.defaultCenter().addObserver_selector_name_object_( self, self.updateCycleMenu, "TorrentStatusChanged", None) NSNotificationCenter.defaultCenter().addObserver_selector_name_object_( self, self.updateCycleMenu, "TorrentSelectionChanged", None) if defaults.objectForKey_(VERSIONCHECK): self.checkVersion() # open any torrents waiting self.otorrent(None)