示例#1
0
    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)
示例#3
0
    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
示例#4
0
    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
示例#5
0
 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)
示例#6
0
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
示例#7
0
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
示例#8
0
    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")
示例#9
0
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
示例#10
0
    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")
示例#11
0
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()
示例#12
0
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)
示例#13
0
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
示例#14
0
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)