Exemplo n.º 1
0
class ABCApp(object):

    def __init__(self, params, installdir, autoload_discovery=True,
                 use_torrent_search=True, use_channel_search=True):
        assert not isInIOThread(), "isInIOThread() seems to not be working correctly"
        self._logger = logging.getLogger(self.__class__.__name__)

        self.params = params
        self.installdir = installdir

        self.state_dir = None
        self.error = None
        self.last_update = 0
        self.ready = False
        self.done = False
        self.frame = None
        self.upgrader = None

        self.said_start_playback = False
        self.decodeprogress = 0

        self.old_reputation = 0

        # DISPERSY will be set when available
        self.dispersy = None
        # BARTER_COMMUNITY will be set when both Dispersy and the EffortCommunity are available
        self.barter_community = None
        self.tunnel_community = None

        self.torrentfeed = None
        self.webUI = None
        self.utility = None

        # Stage 1 start
        session = self.InitStage1(installdir, autoload_discovery=autoload_discovery,
                                  use_torrent_search=use_torrent_search, use_channel_search=use_channel_search)

        self.splash = None
        try:
            bm = self.gui_image_manager.getImage(u'splash.png')
            self.splash = GaugeSplash(bm, "Loading...", 13)
            self.splash.Show()

            self._logger.info('Client Starting Up.')
            self._logger.info("Tribler is using %s as working directory", self.installdir)

            # Stage 2: show the splash window and start the session

            self.splash.tick('Starting API')
            s = self.startAPI(session, self.splash.tick)

            self.utility = Utility(self.installdir, s.get_state_dir())

            if self.utility.read_config(u'saveas', u'downloadconfig'):
                DefaultDownloadStartupConfig.getInstance().set_dest_dir(self.utility.read_config(u'saveas', u'downloadconfig'))

            self.utility.set_app(self)
            self.utility.set_session(s)
            self.guiUtility = GUIUtility.getInstance(self.utility, self.params, self)
            GUIDBProducer.getInstance()

            self._logger.info('Tribler Version: %s Build: %s', version_id, commit_id)

            version_info = self.utility.read_config('version_info')
            if version_info.get('version_id', None) != version_id:
                # First run of a different version
                version_info['first_run'] = int(time())
                version_info['version_id'] = version_id
                self.utility.write_config('version_info', version_info)

            self.splash.tick('Starting session and upgrading database (it may take a while)')
            s.start()
            self.dispersy = s.lm.dispersy

            self.splash.tick('Loading userdownloadchoice')
            from Tribler.Main.vwxGUI.UserDownloadChoice import UserDownloadChoice
            UserDownloadChoice.get_singleton().set_utility(self.utility)

            self.splash.tick('Initializing Family Filter')
            cat = Category.getInstance(session)

            state = self.utility.read_config('family_filter')
            if state in (1, 0):
                cat.set_family_filter(state == 1)
            else:
                self.utility.write_config('family_filter', 1)
                self.utility.flush_config()

                cat.set_family_filter(True)

            # Create global speed limits
            self.splash.tick('Setting up speed limits')

            # Counter to suppress some event from occurring
            self.ratestatecallbackcount = 0

            maxup = self.utility.read_config('maxuploadrate')
            maxdown = self.utility.read_config('maxdownloadrate')
            # set speed limits using LibtorrentMgr
            s.set_max_upload_speed(maxup)
            s.set_max_download_speed(maxdown)

            # Only allow updates to come in after we defined ratelimiter
            self.prevActiveDownloads = []
            s.set_download_states_callback(self.sesscb_states_callback)

            # Schedule task for checkpointing Session, to avoid hash checks after
            # crashes.
            startWorker(consumer=None, workerFn=self.guiservthread_checkpoint_timer, delay=SESSION_CHECKPOINT_INTERVAL)

            if not ALLOW_MULTIPLE:
                # Put it here so an error is shown in the startup-error popup
                # Start server for instance2instance communication
                Instance2InstanceServer(self.utility.read_config('i2ilistenport'), self.i2ithread_readlinecallback)

            self.splash.tick('GUIUtility register')
            self.guiUtility.register()

            self.frame = MainFrame(self,
                                   None,
                                   PLAYBACKMODE_INTERNAL in return_feasible_playback_modes(),
                                   self.splash.tick)
            self.frame.SetIcon(wx.Icon(os.path.join(self.installdir, 'Tribler',
                                                    'Main', 'vwxGUI', 'images',
                                                    'tribler.ico'),
                                       wx.BITMAP_TYPE_ICO))

            # Arno, 2011-06-15: VLC 1.1.10 pops up separate win, don't have two.
            self.frame.videoframe = None
            if PLAYBACKMODE_INTERNAL in return_feasible_playback_modes():
                vlcwrap = s.lm.videoplayer.get_vlcwrap()
                wx.CallLater(3000, vlcwrap._init_vlc)
                self.frame.videoframe = VideoDummyFrame(self.frame.videoparentpanel, self.utility, vlcwrap)

            if sys.platform == 'win32':
                wx.CallAfter(self.frame.top_bg.Refresh)
                wx.CallAfter(self.frame.top_bg.Layout)
            else:
                self.frame.top_bg.Layout()

            # Arno, 2007-05-03: wxWidgets 2.8.3.0 and earlier have the MIME-type for .bmp
            # files set to 'image/x-bmp' whereas 'image/bmp' is the official one.
            try:
                bmphand = None
                hands = wx.Image.GetHandlers()
                for hand in hands:
                    # print "Handler",hand.GetExtension(),hand.GetType(),hand.GetMimeType()
                    if hand.GetMimeType() == 'image/x-bmp':
                        bmphand = hand
                        break
                # wx.Image.AddHandler()
                if bmphand is not None:
                    bmphand.SetMimeType('image/bmp')
            except:
                # wx < 2.7 don't like wx.Image.GetHandlers()
                print_exc()

            self.splash.Destroy()
            self.frame.Show(True)
            session.lm.threadpool.call_in_thread(0, self.guiservthread_free_space_check)

            self.torrentfeed = RssParser.getInstance()

            self.webUI = None
            if self.utility.read_config('use_webui'):
                try:
                    from Tribler.Main.webUI.webUI import WebUI
                    self.webUI = WebUI.getInstance(self.guiUtility.library_manager,
                                                   self.guiUtility.torrentsearch_manager,
                                                   self.utility.read_config('webui_port'))
                    self.webUI.start()
                except Exception:
                    print_exc()

            self.emercoin_mgr = None
            try:
                from Tribler.Main.Emercoin.EmercoinMgr import EmercoinMgr
                self.emercoin_mgr = EmercoinMgr(self.utility)
            except Exception:
                print_exc()

            wx.CallAfter(self.PostInit2)

            # 08/02/10 Boudewijn: Working from home though console
            # doesn't allow me to press close.  The statement below
            # gracefully closes Tribler after 120 seconds.
            # wx.CallLater(120*1000, wx.GetApp().Exit)

            self.ready = True

        except Exception as e:
            if self.splash:
                self.splash.Destroy()

            self.onError(e)

    def InitStage1(self, installdir, autoload_discovery=True,
                   use_torrent_search=True, use_channel_search=True):
        """ Stage 1 start: pre-start the session to handle upgrade.
        """

        # Make sure the installation dir is on the PATH
        os.environ['PATH'] += os.pathsep + os.path.abspath(installdir)

        self.gui_image_manager = GuiImageManager.getInstance(installdir)

        # Start Tribler Session
        defaultConfig = SessionStartupConfig()
        state_dir = defaultConfig.get_state_dir()

        # Switch to the state dir so relative paths can be used (IE, in LevelDB store paths)
        if not os.path.exists(state_dir):
            os.makedirs(state_dir)
        os.chdir(state_dir)

        cfgfilename = Session.get_default_config_filename(state_dir)

        self._logger.debug(u"Session config %s", cfgfilename)
        try:
            self.sconfig = SessionStartupConfig.load(cfgfilename)
        except:
            try:
                self.sconfig = convertSessionConfig(os.path.join(state_dir, 'sessconfig.pickle'), cfgfilename)
                convertMainConfig(state_dir, os.path.join(state_dir, 'abc.conf'),
                                  os.path.join(state_dir, STATEDIR_GUICONFIG))
            except:
                self.sconfig = SessionStartupConfig()
                self.sconfig.set_state_dir(state_dir)

        self.sconfig.set_install_dir(self.installdir)

        # TODO(emilon): Do we still want to force limit this? With the new
        # torrent store it should be pretty fast even with more that that.

        # Arno, 2010-03-31: Hard upgrade to 50000 torrents collected
        self.sconfig.set_torrent_collecting_max_torrents(50000)

        dlcfgfilename = get_default_dscfg_filename(self.sconfig.get_state_dir())
        self._logger.debug("main: Download config %s", dlcfgfilename)
        try:
            defaultDLConfig = DefaultDownloadStartupConfig.load(dlcfgfilename)
        except:
            try:
                defaultDLConfig = convertDefaultDownloadConfig(
                    os.path.join(state_dir, 'dlconfig.pickle'), dlcfgfilename)
            except:
                defaultDLConfig = DefaultDownloadStartupConfig.getInstance()

        if not defaultDLConfig.get_dest_dir():
            defaultDLConfig.set_dest_dir(get_default_dest_dir())
        if not os.path.isdir(defaultDLConfig.get_dest_dir()):
            try:
                os.makedirs(defaultDLConfig.get_dest_dir())
            except:
                # Could not create directory, ask user to select a different location
                dlg = wx.DirDialog(None,
                                   "Could not find download directory, please select a new location to store your downloads",
                                   style=wx.DEFAULT_DIALOG_STYLE)
                dlg.SetPath(get_default_dest_dir())
                if dlg.ShowModal() == wx.ID_OK:
                    new_dest_dir = dlg.GetPath()
                    defaultDLConfig.set_dest_dir(new_dest_dir)
                    defaultDLConfig.save(dlcfgfilename)
                    self.sconfig.save(cfgfilename)
                else:
                    # Quit
                    self.onError = lambda e: self._logger.error(
                        "tribler: quitting due to non-existing destination directory")
                    raise Exception()

        if not use_torrent_search:
            self.sconfig.set_enable_torrent_search(False)
        if not use_channel_search:
            self.sconfig.set_enable_channel_search(False)

        session = Session(self.sconfig, autoload_discovery=autoload_discovery)
        session.add_observer(self.show_upgrade_dialog, NTFY_UPGRADER, [NTFY_STARTED])
        self.upgrader = session.prestart()

        while not self.upgrader.is_done:
            wx.SafeYield()
            sleep(0.1)

        return session

    @forceWxThread
    def show_upgrade_dialog(self, subject, changetype, objectID, *args):
        assert wx.Thread_IsMain()

        upgrade_dialog = TriblerUpgradeDialog(self.gui_image_manager, self.upgrader)
        failed = upgrade_dialog.ShowModal()
        upgrade_dialog.Destroy()
        if failed:
            wx.MessageDialog(None, "Failed to upgrade the on disk data.\n\n"
                             "Tribler has backed up the old data and will now start from scratch.\n\n"
                             "Get in contact with the Tribler team if you want to help debugging this issue.\n\n"
                             "Error was: %s" % self.upgrader.current_status,
                             "Data format upgrade failed", wx.OK | wx.CENTRE | wx.ICON_EXCLAMATION).ShowModal()

    def _frame_and_ready(self):
        return self.ready and self.frame and self.frame.ready

    def PostInit2(self):
        self.frame.Raise()
        self.startWithRightView()
        self.set_reputation()

        s = self.utility.session
        s.add_observer(self.sesscb_ntfy_reachable, NTFY_REACHABLE, [NTFY_INSERT])
        s.add_observer(self.sesscb_ntfy_activities, NTFY_ACTIVITIES, [NTFY_INSERT], cache=10)
        s.add_observer(self.sesscb_ntfy_channelupdates,
                       NTFY_CHANNELCAST, [NTFY_INSERT, NTFY_UPDATE, NTFY_CREATE, NTFY_STATE, NTFY_MODIFIED],
                       cache=10)
        s.add_observer(self.sesscb_ntfy_channelupdates, NTFY_VOTECAST, [NTFY_UPDATE], cache=10)
        s.add_observer(self.sesscb_ntfy_myprefupdates, NTFY_MYPREFERENCES, [NTFY_INSERT, NTFY_UPDATE, NTFY_DELETE])
        s.add_observer(self.sesscb_ntfy_torrentupdates, NTFY_TORRENTS, [NTFY_UPDATE, NTFY_INSERT], cache=10)
        s.add_observer(self.sesscb_ntfy_playlistupdates, NTFY_PLAYLISTS, [NTFY_INSERT, NTFY_UPDATE])
        s.add_observer(self.sesscb_ntfy_commentupdates, NTFY_COMMENTS, [NTFY_INSERT, NTFY_DELETE])
        s.add_observer(self.sesscb_ntfy_modificationupdates, NTFY_MODIFICATIONS, [NTFY_INSERT])
        s.add_observer(self.sesscb_ntfy_moderationupdats, NTFY_MODERATIONS, [NTFY_INSERT])
        s.add_observer(self.sesscb_ntfy_markingupdates, NTFY_MARKINGS, [NTFY_INSERT])
        s.add_observer(self.sesscb_ntfy_torrentfinished, NTFY_TORRENTS, [NTFY_FINISHED])
        s.add_observer(self.sesscb_ntfy_magnet,
                       NTFY_TORRENTS, [NTFY_MAGNET_GOT_PEERS, NTFY_MAGNET_STARTED, NTFY_MAGNET_CLOSE])

        # TODO(emilon): Use the LogObserver I already implemented
        # self.dispersy.callback.attach_exception_handler(self.frame.exceptionHandler)

        startWorker(None, self.loadSessionCheckpoint, delay=5.0, workerType="ThreadPool")

        # initialize the torrent feed thread
        channelcast = s.open_dbhandler(NTFY_CHANNELCAST)

        def db_thread():
            return channelcast.getMyChannelId()

        def wx_thread(delayedResult):
            my_channel = delayedResult.get()
            if my_channel:
                self.torrentfeed.register(self.utility.session, my_channel)
                self.torrentfeed.addCallback(my_channel, self.guiUtility.channelsearch_manager.createTorrentFromDef)

        startWorker(wx_thread, db_thread, delay=5.0)

    def startAPI(self, session, progress):
        @call_on_reactor_thread
        def define_communities(*args):
            assert isInIOThread()
            from Tribler.community.channel.community import ChannelCommunity
            from Tribler.community.channel.preview import PreviewChannelCommunity
            from Tribler.community.tunnel.tunnel_community import TunnelSettings
            from Tribler.community.tunnel.hidden_community import HiddenTunnelCommunity

            # make sure this is only called once
            session.remove_observer(define_communities)

            dispersy = session.get_dispersy_instance()

            self._logger.info("tribler: Preparing communities...")
            now = time()

            dispersy.attach_progress_handler(self.progressHandler)

            default_kwargs = {'tribler_session': session}
            # must be called on the Dispersy thread
            if session.get_barter_community_enabled():
                from Tribler.community.bartercast4.community import BarterCommunity
                dispersy.define_auto_load(BarterCommunity, session.dispersy_member, load=True)

            # load metadata community
            dispersy.define_auto_load(ChannelCommunity, session.dispersy_member, load=True, kargs=default_kwargs)
            dispersy.define_auto_load(PreviewChannelCommunity, session.dispersy_member, kargs=default_kwargs)

            keypair = dispersy.crypto.generate_key(u"curve25519")
            dispersy_member = dispersy.get_member(private_key=dispersy.crypto.key_to_bin(keypair),)
            settings = TunnelSettings(session.get_install_dir(), tribler_session=session)
            tunnel_kwargs = {'tribler_session': session, 'settings': settings}

            self.tunnel_community = dispersy.define_auto_load(HiddenTunnelCommunity, dispersy_member, load=True,
                                                              kargs=tunnel_kwargs)[0]

            session.set_anon_proxy_settings(2, ("127.0.0.1", session.get_tunnel_community_socks5_listen_ports()))

            diff = time() - now
            self._logger.info("tribler: communities are ready in %.2f seconds", diff)

        session.add_observer(define_communities, NTFY_DISPERSY, [NTFY_STARTED])

        return session

    @forceWxThread
    def sesscb_ntfy_myprefupdates(self, subject, changeType, objectID, *args):
        if self._frame_and_ready():
            if changeType in [NTFY_INSERT, NTFY_UPDATE]:
                if changeType == NTFY_INSERT:
                    if self.frame.searchlist:
                        manager = self.frame.searchlist.GetManager()
                        manager.downloadStarted(objectID)

                    manager = self.frame.selectedchannellist.GetManager()
                    manager.downloadStarted(objectID)

                manager = self.frame.librarylist.GetManager()
                manager.downloadStarted(objectID)
            elif changeType == NTFY_DELETE:
                self.guiUtility.frame.librarylist.RemoveItem(objectID)

                if self.guiUtility.frame.librarylist.IsShownOnScreen() and \
                   self.guiUtility.frame.librarydetailspanel.torrent and \
                   self.guiUtility.frame.librarydetailspanel.torrent.infohash == objectID:
                    self.guiUtility.frame.librarylist.ResetBottomWindow()
                    self.guiUtility.frame.top_bg.ClearButtonHandlers()

                if self.guiUtility.frame.librarylist.list.IsEmpty():
                    self.guiUtility.frame.librarylist.SetData([])

    def progressHandler(self, title, message, maximum):
        from Tribler.Main.Dialogs.ThreadSafeProgressDialog import ThreadSafeProgressDialog
        return ThreadSafeProgressDialog(title,
                                        message,
                                        maximum,
                                        None,
                                        wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME |
                                        wx.PD_ESTIMATED_TIME | wx.PD_REMAINING_TIME |
                                        wx.PD_AUTO_HIDE)

    def set_reputation(self):
        def do_db():
            nr_connections = 0
            nr_channel_connections = 0
            if self.dispersy:
                for community in self.dispersy.get_communities():
                    from Tribler.community.search.community import SearchCommunity
                    from Tribler.community.allchannel.community import AllChannelCommunity

                    if isinstance(community, SearchCommunity):
                        nr_connections = community.get_nr_connections()
                    elif isinstance(community, AllChannelCommunity):
                        nr_channel_connections = community.get_nr_connections()

            return nr_connections, nr_channel_connections

        def do_wx(delayedResult):
            if not self.frame:
                return

            nr_connections, nr_channel_connections = delayedResult.get()

            # self.frame.SRstatusbar.set_reputation(myRep, total_down, total_up)

            # bitmap is 16px wide, -> but first and last pixel do not add anything.
            percentage = min(1.0, (nr_connections + 1) / 16.0)
            self.frame.SRstatusbar.SetConnections(percentage, nr_connections, nr_channel_connections)

        """ set the reputation in the GUI"""
        if self._frame_and_ready():
            startWorker(do_wx, do_db, uId=u"tribler.set_reputation")
        startWorker(None, self.set_reputation, delay=5.0, workerType="ThreadPool")

    def sesscb_states_callback(self, dslist):
        if not self.ready:
            return 5.0, []

        wantpeers = []
        self.ratestatecallbackcount += 1
        try:
            # Print stats on Console
            if self.ratestatecallbackcount % 5 == 0:
                for ds in dslist:
                    safename = repr(ds.get_download().get_def().get_name())
                    self._logger.debug(
                        "%s %s %.1f%% dl %.1f ul %.1f n %d",
                        safename,
                        dlstatus_strings[ds.get_status()],
                        100.0 * ds.get_progress(),
                        ds.get_current_speed(DOWNLOAD),
                        ds.get_current_speed(UPLOAD),
                        ds.get_num_peers())
                    if ds.get_status() == DLSTATUS_STOPPED_ON_ERROR:
                        self._logger.error("main: Error: %s", repr(ds.get_error()))
                        download = self.utility.session.lm.downloads.get(ds.get_infohash())
                        if download:
                            download.stop()

                        # show error dialog
                        dlg = wx.MessageDialog(self.frame,
                                               "Download stopped on error: %s" % repr(ds.get_error()),
                                               "Download Error",
                                               wx.OK | wx.ICON_ERROR)
                        dlg.ShowModal()
                        dlg.Destroy()

            # Pass DownloadStates to libaryView
            no_collected_list = [ds for ds in dslist]
            try:
                # Arno, 2012-07-17: Retrieving peerlist for the DownloadStates takes CPU
                # so only do it when needed for display.
                wantpeers.extend(self.guiUtility.library_manager.download_state_callback(no_collected_list))
            except:
                print_exc()

            # Check to see if a download has finished
            newActiveDownloads = []
            doCheckpoint = False
            seeding_download_list = []
            for ds in dslist:
                state = ds.get_status()
                download = ds.get_download()
                tdef = download.get_def()
                safename = tdef.get_name_as_unicode()

                if state == DLSTATUS_DOWNLOADING:
                    newActiveDownloads.append(safename)

                elif state == DLSTATUS_SEEDING:
                    seeding_download_list.append({u'infohash': tdef.get_infohash(),
                                                  u'download': download,
                                                  })

                    if safename in self.prevActiveDownloads:
                        infohash = tdef.get_infohash()

                        self.utility.session.notifier.notify(NTFY_TORRENTS, NTFY_FINISHED, infohash, safename)

                        doCheckpoint = True

                    elif download.get_hops() == 0 and download.get_safe_seeding():
                        self._logger.info("Re-add torrent with default nr of hops to prevent naked seeding")
                        self.utility.session.remove_download(download)

                        # copy the old download_config and change the hop count
                        dscfg = download.copy()
                        dscfg.set_hops(self.utility.read_config('default_number_hops'))

                        # TODO(emilon): That's a hack to work around the fact
                        # that removing a torrent is racy.
                        self.utility.session.lm.threadpool.call(0.5,
                                                                reactor.callInThread,
                                                                self.utility.session.start_download,
                                                                tdef, dscfg)

            self.prevActiveDownloads = newActiveDownloads
            if doCheckpoint:
                self.utility.session.checkpoint()

            if self.utility.read_config(u'seeding_mode') == 'never':
                for data in seeding_download_list:
                    data[u'download'].stop()
                    from Tribler.Main.vwxGUI.UserDownloadChoice import UserDownloadChoice
                    UserDownloadChoice.get_singleton().set_download_state(data[u'infohash'], "stop")

            # Adjust speeds and call TunnelCommunity.monitor_downloads once every 4 seconds
            adjustspeeds = False
            if self.ratestatecallbackcount % 4 == 0:
                adjustspeeds = True

            if adjustspeeds and self.tunnel_community:
                self.tunnel_community.monitor_downloads(dslist)

        except:
            print_exc()

        return 1.0, wantpeers

    def loadSessionCheckpoint(self):
        pstate_dir = self.utility.session.get_downloads_pstate_dir()

        filelist = os.listdir(pstate_dir)
        if any([filename.endswith('.pickle') for filename in filelist]):
            convertDownloadCheckpoints(pstate_dir)

        from Tribler.Main.vwxGUI.UserDownloadChoice import UserDownloadChoice
        user_download_choice = UserDownloadChoice.get_singleton()
        initialdlstatus_dict = {}
        for infohash, state in user_download_choice.get_download_states().iteritems():
            if state == 'stop':
                initialdlstatus_dict[infohash] = DLSTATUS_STOPPED

        self.utility.session.load_checkpoint(initialdlstatus_dict=initialdlstatus_dict)

    def guiservthread_free_space_check(self):
        if not (self and self.frame and self.frame.SRstatusbar):
            return

        free_space = get_free_space(DefaultDownloadStartupConfig.getInstance().get_dest_dir())
        self.frame.SRstatusbar.RefreshFreeSpace(free_space)

        storage_locations = defaultdict(list)
        for download in self.utility.session.get_downloads():
            if download.get_status() == DLSTATUS_DOWNLOADING:
                storage_locations[download.get_dest_dir()].append(download)

        show_message = False
        low_on_space = [
            path for path in storage_locations.keys(
            ) if 0 < get_free_space(
                path) < self.utility.read_config(
                'free_space_threshold')]
        for path in low_on_space:
            for download in storage_locations[path]:
                download.stop()
                show_message = True

        if show_message:
            wx.CallAfter(wx.MessageBox, "Tribler has detected low disk space. Related downloads have been stopped.",
                         "Error")

        self.utility.session.lm.threadpool.call_in_thread(FREE_SPACE_CHECK_INTERVAL, self.guiservthread_free_space_check)

    def guiservthread_checkpoint_timer(self):
        """ Periodically checkpoint Session """
        if self.done:
            return
        try:
            self._logger.info("main: Checkpointing Session")
            self.utility.session.checkpoint()

            self.utility.session.lm.threadpool.call_in_thread(SESSION_CHECKPOINT_INTERVAL, self.guiservthread_checkpoint_timer)
        except:
            print_exc()

    @forceWxThread
    def sesscb_ntfy_activities(self, events):
        if self._frame_and_ready():
            for args in events:
                objectID = args[2]
                args = args[3:]

                self.frame.setActivity(objectID, *args)

    @forceWxThread
    def sesscb_ntfy_reachable(self, subject, changeType, objectID, msg):
        if self._frame_and_ready():
            self.frame.SRstatusbar.onReachable()

    @forceWxThread
    def sesscb_ntfy_channelupdates(self, events):
        if self._frame_and_ready():
            for args in events:
                subject = args[0]
                changeType = args[1]
                objectID = args[2]

                if self.frame.channellist:
                    if len(args) > 3:
                        myvote = args[3]
                    else:
                        myvote = False

                    manager = self.frame.channellist.GetManager()
                    manager.channelUpdated(objectID, subject == NTFY_VOTECAST, myvote=myvote)

                manager = self.frame.selectedchannellist.GetManager()
                manager.channelUpdated(
                    objectID,
                    stateChanged=changeType == NTFY_STATE,
                    modified=changeType == NTFY_MODIFIED)

                if changeType == NTFY_CREATE:
                    if self.frame.channellist:
                        self.frame.channellist.SetMyChannelId(objectID)

                    self.torrentfeed.register(self.utility.session, objectID)
                    self.torrentfeed.addCallback(objectID, self.guiUtility.channelsearch_manager.createTorrentFromDef)

                self.frame.managechannel.channelUpdated(
                    objectID,
                    created=changeType == NTFY_CREATE,
                    modified=changeType == NTFY_MODIFIED)

    @forceWxThread
    def sesscb_ntfy_torrentupdates(self, events):
        if self._frame_and_ready():
            infohashes = [args[2] for args in events]

            if self.frame.searchlist:
                manager = self.frame.searchlist.GetManager()
                manager.torrentsUpdated(infohashes)

                manager = self.frame.selectedchannellist.GetManager()
                manager.torrentsUpdated(infohashes)

                manager = self.frame.playlist.GetManager()
                manager.torrentsUpdated(infohashes)

                manager = self.frame.librarylist.GetManager()
                manager.torrentsUpdated(infohashes)

            from Tribler.Main.Utility.GuiDBTuples import CollectedTorrent

            if self.frame.torrentdetailspanel.torrent and self.frame.torrentdetailspanel.torrent.infohash in infohashes:
                # If an updated torrent is being shown in the detailspanel, make sure the information gets refreshed.
                t = self.frame.torrentdetailspanel.torrent
                torrent = t.torrent if isinstance(t, CollectedTorrent) else t
                self.frame.torrentdetailspanel.setTorrent(torrent)

            if self.frame.librarydetailspanel.torrent and self.frame.librarydetailspanel.torrent.infohash in infohashes:
                t = self.frame.librarydetailspanel.torrent
                torrent = t.torrent if isinstance(t, CollectedTorrent) else t
                self.frame.librarydetailspanel.setTorrent(torrent)

    def sesscb_ntfy_torrentfinished(self, subject, changeType, objectID, *args):
        self.guiUtility.Notify(
            "Download Completed", "Torrent '%s' has finished downloading. Now seeding." %
            args[0], icon='seed')

        if self._frame_and_ready():
            infohash = objectID
            torrent = self.guiUtility.torrentsearch_manager.getTorrentByInfohash(infohash)
            self.guiUtility.library_manager.addDownloadState(torrent)

    def sesscb_ntfy_magnet(self, subject, changetype, objectID, *args):
        if changetype == NTFY_MAGNET_STARTED:
            self.guiUtility.library_manager.magnet_started(objectID)
        elif changetype == NTFY_MAGNET_GOT_PEERS:
            self.guiUtility.library_manager.magnet_got_peers(objectID, args[0])
        elif changetype == NTFY_MAGNET_CLOSE:
            self.guiUtility.library_manager.magnet_close(objectID)

    @forceWxThread
    def sesscb_ntfy_playlistupdates(self, subject, changeType, objectID, *args):
        if self._frame_and_ready():
            if changeType == NTFY_INSERT:
                self.frame.managechannel.playlistCreated(objectID)

                manager = self.frame.selectedchannellist.GetManager()
                manager.playlistCreated(objectID)

            else:
                self.frame.managechannel.playlistUpdated(objectID, modified=changeType == NTFY_MODIFIED)

                if len(args) > 0:
                    infohash = args[0]
                else:
                    infohash = False
                manager = self.frame.selectedchannellist.GetManager()
                manager.playlistUpdated(objectID, infohash, modified=changeType == NTFY_MODIFIED)

                manager = self.frame.playlist.GetManager()
                manager.playlistUpdated(objectID, modified=changeType == NTFY_MODIFIED)

    @forceWxThread
    def sesscb_ntfy_commentupdates(self, subject, changeType, objectID, *args):
        if self._frame_and_ready():
            self.frame.selectedchannellist.OnCommentCreated(objectID)
            self.frame.playlist.OnCommentCreated(objectID)

    @forceWxThread
    def sesscb_ntfy_modificationupdates(self, subject, changeType, objectID, *args):
        if self._frame_and_ready():
            self.frame.selectedchannellist.OnModificationCreated(objectID)
            self.frame.playlist.OnModificationCreated(objectID)

    @forceWxThread
    def sesscb_ntfy_moderationupdats(self, subject, changeType, objectID, *args):
        if self._frame_and_ready():
            self.frame.selectedchannellist.OnModerationCreated(objectID)
            self.frame.playlist.OnModerationCreated(objectID)

    @forceWxThread
    def sesscb_ntfy_markingupdates(self, subject, changeType, objectID, *args):
        if self._frame_and_ready():
            self.frame.selectedchannellist.OnMarkingCreated(objectID)
            self.frame.playlist.OnModerationCreated(objectID)

    @forceWxThread
    def onError(self, e):
        print_exc()
        _, value, stack = sys.exc_info()
        backtrace = traceback.format_exception(type, value, stack)

        win = FeedbackWindow("Unfortunately, Tribler ran into an internal error")
        win.CreateOutputWindow('')
        for line in backtrace:
            win.write(line)

        win.ShowModal()

    @forceWxThread
    def OnExit(self):
        bm = self.gui_image_manager.getImage(u'closescreen.png')
        self.closewindow = GaugeSplash(bm, "Closing...", 6)
        self.closewindow.Show()

        self._logger.info("main: ONEXIT")
        self.ready = False
        self.done = True

        # write all persistent data to disk
        self.closewindow.tick('Write all persistent data to disk')
        if self.torrentfeed:
            self.torrentfeed.shutdown()
            self.torrentfeed.delInstance()
        if self.webUI:
            self.webUI.stop()
            self.webUI.delInstance()

        if self.frame:
            self.frame.Destroy()
            self.frame = None

        # Don't checkpoint, interferes with current way of saving Preferences,
        # see Tribler/Main/Dialogs/abcoption.py
        if self.utility:
            # Niels: lets add a max waiting time for this session shutdown.
            session_shutdown_start = time()

            try:
                self._logger.info("ONEXIT cleaning database")
                self.closewindow.tick('Cleaning database')
                torrent_db = self.utility.session.open_dbhandler(NTFY_TORRENTS)
                torrent_db._db.clean_db(randint(0, 24) == 0, exiting=True)
            except:
                print_exc()

            self.closewindow.tick('Shutdown session')
            self.utility.session.shutdown(hacksessconfcheckpoint=False)

            # Arno, 2012-07-12: Shutdown should be quick
            # Niels, 2013-03-21: However, setting it too low will prevent checkpoints from being written to disk
            waittime = 60
            while not self.utility.session.has_shutdown():
                diff = time() - session_shutdown_start
                if diff > waittime:
                    self._logger.info("main: ONEXIT NOT Waiting for Session to shutdown, took too long")
                    break

                self._logger.info(
                    "ONEXIT Waiting for Session to shutdown, will wait for an additional %d seconds",
                    waittime - diff)
                sleep(3)
            self._logger.info("ONEXIT Session is shutdown")

        self.closewindow.tick('Deleting instances')
        self._logger.debug("ONEXIT deleting instances")

        Session.del_instance()
        GUIUtility.delInstance()
        GUIDBProducer.delInstance()
        DefaultDownloadStartupConfig.delInstance()
        GuiImageManager.delInstance()

        self.closewindow.tick('Exiting now')

        self.closewindow.Destroy()

        return 0

    def db_exception_handler(self, e):
        self._logger.debug("Database Exception handler called %s value %s #", e, e.args)
        try:
            if e.args[1] == "DB object has been closed":
                return  # We caused this non-fatal error, don't show.
            if self.error is not None and self.error.args[1] == e.args[1]:
                return  # don't repeat same error
        except:
            self._logger.error("db_exception_handler error %s %s", e, type(e))
            print_exc()
            # print_stack()

        self.onError(e)

    def getConfigPath(self):
        return self.utility.getConfigPath()

    def startWithRightView(self):
        if self.params[0] != "":
            self.guiUtility.ShowPage('my_files')

    def i2ithread_readlinecallback(self, ic, cmd):
        """ Called by Instance2Instance thread """

        self._logger.info("main: Another instance called us with cmd %s", cmd)
        ic.close()

        if cmd.startswith('START '):
            param = cmd[len('START '):].strip().decode("utf-8")
            torrentfilename = None
            if param.startswith('http:'):
                # Retrieve from web
                f = tempfile.NamedTemporaryFile()
                n = urllib2.urlopen(param)
                data = n.read()
                f.write(data)
                f.close()
                n.close()
                torrentfilename = f.name
            else:
                torrentfilename = param

            # Switch to GUI thread
            # New for 5.0: Start in VOD mode
            def start_asked_download():
                if torrentfilename.startswith("magnet:"):
                    self.frame.startDownloadFromMagnet(torrentfilename)
                else:
                    self.frame.startDownload(torrentfilename)
                self.guiUtility.ShowPage('my_files')

            wx.CallAfter(start_asked_download)
Exemplo n.º 2
0
class ABCApp(object):
    def __init__(self,
                 params,
                 installdir,
                 autoload_discovery=True,
                 use_torrent_search=True,
                 use_channel_search=True):
        assert not isInIOThread(
        ), "isInIOThread() seems to not be working correctly"
        self._logger = logging.getLogger(self.__class__.__name__)

        self.params = params
        self.installdir = installdir

        self.state_dir = None
        self.error = None
        self.last_update = 0
        self.ready = False
        self.done = False
        self.frame = None
        self.upgrader = None

        self.said_start_playback = False
        self.decodeprogress = 0

        self.old_reputation = 0

        # DISPERSY will be set when available
        self.dispersy = None
        # BARTER_COMMUNITY will be set when both Dispersy and the EffortCommunity are available
        self.barter_community = None
        self.tunnel_community = None

        self.torrentfeed = None
        self.webUI = None
        self.utility = None

        # Stage 1 start
        session = self.InitStage1(installdir,
                                  autoload_discovery=autoload_discovery,
                                  use_torrent_search=use_torrent_search,
                                  use_channel_search=use_channel_search)

        self.splash = None
        try:
            bm = self.gui_image_manager.getImage(u'splash.png')
            self.splash = GaugeSplash(bm, "Loading...", 13)
            self.splash.Show()

            self._logger.info('Client Starting Up.')
            self._logger.info("Tribler is using %s as working directory",
                              self.installdir)

            # Stage 2: show the splash window and start the session

            self.splash.tick('Starting API')
            s = self.startAPI(session, self.splash.tick)

            self.utility = Utility(self.installdir, s.get_state_dir())

            if self.utility.read_config(u'saveas', u'downloadconfig'):
                DefaultDownloadStartupConfig.getInstance().set_dest_dir(
                    self.utility.read_config(u'saveas', u'downloadconfig'))

            self.utility.set_app(self)
            self.utility.set_session(s)
            self.guiUtility = GUIUtility.getInstance(self.utility, self.params,
                                                     self)
            GUIDBProducer.getInstance()

            self._logger.info('Tribler Version: %s Build: %s', version_id,
                              commit_id)

            version_info = self.utility.read_config('version_info')
            if version_info.get('version_id', None) != version_id:
                # First run of a different version
                version_info['first_run'] = int(time())
                version_info['version_id'] = version_id
                self.utility.write_config('version_info', version_info)

            self.splash.tick(
                'Starting session and upgrading database (it may take a while)'
            )
            s.start()
            self.dispersy = s.lm.dispersy

            self.splash.tick('Loading userdownloadchoice')
            from Tribler.Main.vwxGUI.UserDownloadChoice import UserDownloadChoice
            UserDownloadChoice.get_singleton().set_utility(self.utility)

            self.splash.tick('Initializing Family Filter')
            cat = Category.getInstance(session)

            state = self.utility.read_config('family_filter')
            if state in (1, 0):
                cat.set_family_filter(state == 1)
            else:
                self.utility.write_config('family_filter', 1)
                self.utility.flush_config()

                cat.set_family_filter(True)

            # Create global speed limits
            self.splash.tick('Setting up speed limits')

            # Counter to suppress some event from occurring
            self.ratestatecallbackcount = 0

            maxup = self.utility.read_config('maxuploadrate')
            maxdown = self.utility.read_config('maxdownloadrate')
            # set speed limits using LibtorrentMgr
            s.set_max_upload_speed(maxup)
            s.set_max_download_speed(maxdown)

            # Only allow updates to come in after we defined ratelimiter
            self.prevActiveDownloads = []
            s.set_download_states_callback(self.sesscb_states_callback)

            # Schedule task for checkpointing Session, to avoid hash checks after
            # crashes.
            startWorker(consumer=None,
                        workerFn=self.guiservthread_checkpoint_timer,
                        delay=SESSION_CHECKPOINT_INTERVAL)

            if not ALLOW_MULTIPLE:
                # Put it here so an error is shown in the startup-error popup
                # Start server for instance2instance communication
                Instance2InstanceServer(
                    self.utility.read_config('i2ilistenport'),
                    self.i2ithread_readlinecallback)

            self.splash.tick('GUIUtility register')
            self.guiUtility.register()

            self.frame = MainFrame(
                self, None, PLAYBACKMODE_INTERNAL
                in return_feasible_playback_modes(), self.splash.tick)
            self.frame.SetIcon(
                wx.Icon(
                    os.path.join(self.installdir, 'Tribler', 'Main', 'vwxGUI',
                                 'images', 'tribler.ico'), wx.BITMAP_TYPE_ICO))

            # Arno, 2011-06-15: VLC 1.1.10 pops up separate win, don't have two.
            self.frame.videoframe = None
            if PLAYBACKMODE_INTERNAL in return_feasible_playback_modes():
                vlcwrap = s.lm.videoplayer.get_vlcwrap()
                wx.CallLater(3000, vlcwrap._init_vlc)
                self.frame.videoframe = VideoDummyFrame(
                    self.frame.videoparentpanel, self.utility, vlcwrap)

            if sys.platform == 'win32':
                wx.CallAfter(self.frame.top_bg.Refresh)
                wx.CallAfter(self.frame.top_bg.Layout)
            else:
                self.frame.top_bg.Layout()

            # Arno, 2007-05-03: wxWidgets 2.8.3.0 and earlier have the MIME-type for .bmp
            # files set to 'image/x-bmp' whereas 'image/bmp' is the official one.
            try:
                bmphand = None
                hands = wx.Image.GetHandlers()
                for hand in hands:
                    # print "Handler",hand.GetExtension(),hand.GetType(),hand.GetMimeType()
                    if hand.GetMimeType() == 'image/x-bmp':
                        bmphand = hand
                        break
                # wx.Image.AddHandler()
                if bmphand is not None:
                    bmphand.SetMimeType('image/bmp')
            except:
                # wx < 2.7 don't like wx.Image.GetHandlers()
                print_exc()

            self.splash.Destroy()
            self.frame.Show(True)
            session.lm.threadpool.call_in_thread(
                0, self.guiservthread_free_space_check)

            self.torrentfeed = RssParser.getInstance()

            self.webUI = None
            if self.utility.read_config('use_webui'):
                try:
                    from Tribler.Main.webUI.webUI import WebUI
                    self.webUI = WebUI.getInstance(
                        self.guiUtility.library_manager,
                        self.guiUtility.torrentsearch_manager,
                        self.utility.read_config('webui_port'))
                    self.webUI.start()
                except Exception:
                    print_exc()

            self.emercoin_mgr = None
            try:
                from Tribler.Main.Emercoin.EmercoinMgr import EmercoinMgr
                self.emercoin_mgr = EmercoinMgr(self.utility)
            except Exception:
                print_exc()

            wx.CallAfter(self.PostInit2)

            # 08/02/10 Boudewijn: Working from home though console
            # doesn't allow me to press close.  The statement below
            # gracefully closes Tribler after 120 seconds.
            # wx.CallLater(120*1000, wx.GetApp().Exit)

            self.ready = True

        except Exception as e:
            if self.splash:
                self.splash.Destroy()

            self.onError(e)

    def InitStage1(self,
                   installdir,
                   autoload_discovery=True,
                   use_torrent_search=True,
                   use_channel_search=True):
        """ Stage 1 start: pre-start the session to handle upgrade.
        """

        self.gui_image_manager = GuiImageManager.getInstance(installdir)

        # Start Tribler Session
        defaultConfig = SessionStartupConfig()
        state_dir = defaultConfig.get_state_dir()

        # Switch to the state dir so relative paths can be used (IE, in LevelDB store paths)
        if not os.path.exists(state_dir):
            os.makedirs(state_dir)
        os.chdir(state_dir)

        cfgfilename = Session.get_default_config_filename(state_dir)

        self._logger.debug(u"Session config %s", cfgfilename)
        try:
            self.sconfig = SessionStartupConfig.load(cfgfilename)
        except:
            try:
                self.sconfig = convertSessionConfig(
                    os.path.join(state_dir, 'sessconfig.pickle'), cfgfilename)
                convertMainConfig(state_dir,
                                  os.path.join(state_dir, 'abc.conf'),
                                  os.path.join(state_dir, STATEDIR_GUICONFIG))
            except:
                self.sconfig = SessionStartupConfig()
                self.sconfig.set_state_dir(state_dir)

        self.sconfig.set_install_dir(self.installdir)

        # TODO(emilon): Do we still want to force limit this? With the new
        # torrent store it should be pretty fast even with more that that.

        # Arno, 2010-03-31: Hard upgrade to 50000 torrents collected
        self.sconfig.set_torrent_collecting_max_torrents(50000)

        dlcfgfilename = get_default_dscfg_filename(
            self.sconfig.get_state_dir())
        self._logger.debug("main: Download config %s", dlcfgfilename)
        try:
            defaultDLConfig = DefaultDownloadStartupConfig.load(dlcfgfilename)
        except:
            try:
                defaultDLConfig = convertDefaultDownloadConfig(
                    os.path.join(state_dir, 'dlconfig.pickle'), dlcfgfilename)
            except:
                defaultDLConfig = DefaultDownloadStartupConfig.getInstance()

        if not defaultDLConfig.get_dest_dir():
            defaultDLConfig.set_dest_dir(get_default_dest_dir())
        if not os.path.isdir(defaultDLConfig.get_dest_dir()):
            try:
                os.makedirs(defaultDLConfig.get_dest_dir())
            except:
                # Could not create directory, ask user to select a different location
                dlg = wx.DirDialog(
                    None,
                    "Could not find download directory, please select a new location to store your downloads",
                    style=wx.DEFAULT_DIALOG_STYLE)
                dlg.SetPath(get_default_dest_dir())
                if dlg.ShowModal() == wx.ID_OK:
                    new_dest_dir = dlg.GetPath()
                    defaultDLConfig.set_dest_dir(new_dest_dir)
                    defaultDLConfig.save(dlcfgfilename)
                    self.sconfig.save(cfgfilename)
                else:
                    # Quit
                    self.onError = lambda e: self._logger.error(
                        "tribler: quitting due to non-existing destination directory"
                    )
                    raise Exception()

        if not use_torrent_search:
            self.sconfig.set_enable_torrent_search(False)
        if not use_channel_search:
            self.sconfig.set_enable_channel_search(False)

        session = Session(self.sconfig, autoload_discovery=autoload_discovery)
        session.add_observer(self.show_upgrade_dialog, NTFY_UPGRADER,
                             [NTFY_STARTED])
        self.upgrader = session.prestart()

        while not self.upgrader.is_done:
            wx.SafeYield()
            sleep(0.1)

        return session

    @forceWxThread
    def show_upgrade_dialog(self, subject, changetype, objectID, *args):
        assert wx.Thread_IsMain()

        upgrade_dialog = TriblerUpgradeDialog(self.gui_image_manager,
                                              self.upgrader)
        failed = upgrade_dialog.ShowModal()
        upgrade_dialog.Destroy()
        if failed:
            wx.MessageDialog(
                None, "Failed to upgrade the on disk data.\n\n"
                "Tribler has backed up the old data and will now start from scratch.\n\n"
                "Get in contact with the Tribler team if you want to help debugging this issue.\n\n"
                "Error was: %s" % self.upgrader.current_status,
                "Data format upgrade failed",
                wx.OK | wx.CENTRE | wx.ICON_EXCLAMATION).ShowModal()

    def _frame_and_ready(self):
        return self.ready and self.frame and self.frame.ready

    def PostInit2(self):
        self.frame.Raise()
        self.startWithRightView()
        self.set_reputation()

        s = self.utility.session
        s.add_observer(self.sesscb_ntfy_reachable, NTFY_REACHABLE,
                       [NTFY_INSERT])
        s.add_observer(self.sesscb_ntfy_activities,
                       NTFY_ACTIVITIES, [NTFY_INSERT],
                       cache=10)
        s.add_observer(
            self.sesscb_ntfy_channelupdates,
            NTFY_CHANNELCAST,
            [NTFY_INSERT, NTFY_UPDATE, NTFY_CREATE, NTFY_STATE, NTFY_MODIFIED],
            cache=10)
        s.add_observer(self.sesscb_ntfy_channelupdates,
                       NTFY_VOTECAST, [NTFY_UPDATE],
                       cache=10)
        s.add_observer(self.sesscb_ntfy_myprefupdates, NTFY_MYPREFERENCES,
                       [NTFY_INSERT, NTFY_UPDATE, NTFY_DELETE])
        s.add_observer(self.sesscb_ntfy_torrentupdates,
                       NTFY_TORRENTS, [NTFY_UPDATE, NTFY_INSERT],
                       cache=10)
        s.add_observer(self.sesscb_ntfy_playlistupdates, NTFY_PLAYLISTS,
                       [NTFY_INSERT, NTFY_UPDATE])
        s.add_observer(self.sesscb_ntfy_commentupdates, NTFY_COMMENTS,
                       [NTFY_INSERT, NTFY_DELETE])
        s.add_observer(self.sesscb_ntfy_modificationupdates,
                       NTFY_MODIFICATIONS, [NTFY_INSERT])
        s.add_observer(self.sesscb_ntfy_moderationupdats, NTFY_MODERATIONS,
                       [NTFY_INSERT])
        s.add_observer(self.sesscb_ntfy_markingupdates, NTFY_MARKINGS,
                       [NTFY_INSERT])
        s.add_observer(self.sesscb_ntfy_torrentfinished, NTFY_TORRENTS,
                       [NTFY_FINISHED])
        s.add_observer(
            self.sesscb_ntfy_magnet, NTFY_TORRENTS,
            [NTFY_MAGNET_GOT_PEERS, NTFY_MAGNET_STARTED, NTFY_MAGNET_CLOSE])

        # TODO(emilon): Use the LogObserver I already implemented
        # self.dispersy.callback.attach_exception_handler(self.frame.exceptionHandler)

        startWorker(None,
                    self.loadSessionCheckpoint,
                    delay=5.0,
                    workerType="ThreadPool")

        # initialize the torrent feed thread
        channelcast = s.open_dbhandler(NTFY_CHANNELCAST)

        def db_thread():
            return channelcast.getMyChannelId()

        def wx_thread(delayedResult):
            my_channel = delayedResult.get()
            if my_channel:
                self.torrentfeed.register(self.utility.session, my_channel)
                self.torrentfeed.addCallback(
                    my_channel,
                    self.guiUtility.channelsearch_manager.createTorrentFromDef)

        startWorker(wx_thread, db_thread, delay=5.0)

    def startAPI(self, session, progress):
        @call_on_reactor_thread
        def define_communities(*args):
            assert isInIOThread()
            from Tribler.community.channel.community import ChannelCommunity
            from Tribler.community.channel.preview import PreviewChannelCommunity
            from Tribler.community.tunnel.tunnel_community import TunnelSettings
            from Tribler.community.tunnel.hidden_community import HiddenTunnelCommunity

            # make sure this is only called once
            session.remove_observer(define_communities)

            dispersy = session.get_dispersy_instance()

            self._logger.info("tribler: Preparing communities...")
            now = time()

            dispersy.attach_progress_handler(self.progressHandler)

            default_kwargs = {'tribler_session': session}
            # must be called on the Dispersy thread
            if session.get_barter_community_enabled():
                from Tribler.community.bartercast4.community import BarterCommunity
                dispersy.define_auto_load(BarterCommunity,
                                          session.dispersy_member,
                                          load=True)

            # load metadata community
            dispersy.define_auto_load(ChannelCommunity,
                                      session.dispersy_member,
                                      load=True,
                                      kargs=default_kwargs)
            dispersy.define_auto_load(PreviewChannelCommunity,
                                      session.dispersy_member,
                                      kargs=default_kwargs)

            keypair = dispersy.crypto.generate_key(u"curve25519")
            dispersy_member = dispersy.get_member(
                private_key=dispersy.crypto.key_to_bin(keypair), )
            settings = TunnelSettings(session.get_install_dir(),
                                      tribler_session=session)
            tunnel_kwargs = {'tribler_session': session, 'settings': settings}

            self.tunnel_community = dispersy.define_auto_load(
                HiddenTunnelCommunity,
                dispersy_member,
                load=True,
                kargs=tunnel_kwargs)[0]

            session.set_anon_proxy_settings(
                2, ("127.0.0.1",
                    session.get_tunnel_community_socks5_listen_ports()))

            diff = time() - now
            self._logger.info("tribler: communities are ready in %.2f seconds",
                              diff)

        session.add_observer(define_communities, NTFY_DISPERSY, [NTFY_STARTED])

        return session

    @forceWxThread
    def sesscb_ntfy_myprefupdates(self, subject, changeType, objectID, *args):
        if self._frame_and_ready():
            if changeType in [NTFY_INSERT, NTFY_UPDATE]:
                if changeType == NTFY_INSERT:
                    if self.frame.searchlist:
                        manager = self.frame.searchlist.GetManager()
                        manager.downloadStarted(objectID)

                    manager = self.frame.selectedchannellist.GetManager()
                    manager.downloadStarted(objectID)

                manager = self.frame.librarylist.GetManager()
                manager.downloadStarted(objectID)
            elif changeType == NTFY_DELETE:
                self.guiUtility.frame.librarylist.RemoveItem(objectID)

                if self.guiUtility.frame.librarylist.IsShownOnScreen() and \
                   self.guiUtility.frame.librarydetailspanel.torrent and \
                   self.guiUtility.frame.librarydetailspanel.torrent.infohash == objectID:
                    self.guiUtility.frame.librarylist.ResetBottomWindow()
                    self.guiUtility.frame.top_bg.ClearButtonHandlers()

                if self.guiUtility.frame.librarylist.list.IsEmpty():
                    self.guiUtility.frame.librarylist.SetData([])

    def progressHandler(self, title, message, maximum):
        from Tribler.Main.Dialogs.ThreadSafeProgressDialog import ThreadSafeProgressDialog
        return ThreadSafeProgressDialog(
            title, message, maximum, None, wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME
            | wx.PD_ESTIMATED_TIME | wx.PD_REMAINING_TIME | wx.PD_AUTO_HIDE)

    def set_reputation(self):
        def do_db():
            nr_connections = 0
            nr_channel_connections = 0
            if self.dispersy:
                for community in self.dispersy.get_communities():
                    from Tribler.community.search.community import SearchCommunity
                    from Tribler.community.allchannel.community import AllChannelCommunity

                    if isinstance(community, SearchCommunity):
                        nr_connections = community.get_nr_connections()
                    elif isinstance(community, AllChannelCommunity):
                        nr_channel_connections = community.get_nr_connections()

            return nr_connections, nr_channel_connections

        def do_wx(delayedResult):
            if not self.frame:
                return

            nr_connections, nr_channel_connections = delayedResult.get()

            # self.frame.SRstatusbar.set_reputation(myRep, total_down, total_up)

            # bitmap is 16px wide, -> but first and last pixel do not add anything.
            percentage = min(1.0, (nr_connections + 1) / 16.0)
            self.frame.SRstatusbar.SetConnections(percentage, nr_connections,
                                                  nr_channel_connections)

        """ set the reputation in the GUI"""
        if self._frame_and_ready():
            startWorker(do_wx, do_db, uId=u"tribler.set_reputation")
        startWorker(None,
                    self.set_reputation,
                    delay=5.0,
                    workerType="ThreadPool")

    def sesscb_states_callback(self, dslist):
        if not self.ready:
            return 5.0, []

        wantpeers = []
        self.ratestatecallbackcount += 1
        try:
            # Print stats on Console
            if self.ratestatecallbackcount % 5 == 0:
                for ds in dslist:
                    safename = repr(ds.get_download().get_def().get_name())
                    self._logger.debug("%s %s %.1f%% dl %.1f ul %.1f n %d",
                                       safename,
                                       dlstatus_strings[ds.get_status()],
                                       100.0 * ds.get_progress(),
                                       ds.get_current_speed(DOWNLOAD),
                                       ds.get_current_speed(UPLOAD),
                                       ds.get_num_peers())
                    if ds.get_status() == DLSTATUS_STOPPED_ON_ERROR:
                        self._logger.error("main: Error: %s",
                                           repr(ds.get_error()))
                        download = self.utility.session.lm.downloads.get(
                            ds.get_infohash())
                        if download:
                            download.stop()

                        # show error dialog
                        dlg = wx.MessageDialog(
                            self.frame, "Download stopped on error: %s" %
                            repr(ds.get_error()), "Download Error",
                            wx.OK | wx.ICON_ERROR)
                        dlg.ShowModal()
                        dlg.Destroy()

            # Pass DownloadStates to libaryView
            no_collected_list = [ds for ds in dslist]
            try:
                # Arno, 2012-07-17: Retrieving peerlist for the DownloadStates takes CPU
                # so only do it when needed for display.
                wantpeers.extend(
                    self.guiUtility.library_manager.download_state_callback(
                        no_collected_list))
            except:
                print_exc()

            # Check to see if a download has finished
            newActiveDownloads = []
            doCheckpoint = False
            seeding_download_list = []
            for ds in dslist:
                state = ds.get_status()
                download = ds.get_download()
                tdef = download.get_def()
                safename = tdef.get_name_as_unicode()

                if state == DLSTATUS_DOWNLOADING:
                    newActiveDownloads.append(safename)

                elif state == DLSTATUS_SEEDING:
                    seeding_download_list.append({
                        u'infohash':
                        tdef.get_infohash(),
                        u'download':
                        download,
                    })

                    if safename in self.prevActiveDownloads:
                        infohash = tdef.get_infohash()

                        self.utility.session.notifier.notify(
                            NTFY_TORRENTS, NTFY_FINISHED, infohash, safename)

                        doCheckpoint = True

                    elif download.get_hops(
                    ) == 0 and download.get_safe_seeding():
                        self._logger.info(
                            "Re-add torrent with default nr of hops to prevent naked seeding"
                        )
                        self.utility.session.remove_download(download)

                        # copy the old download_config and change the hop count
                        dscfg = download.copy()
                        dscfg.set_hops(
                            self.utility.read_config('default_number_hops'))

                        # TODO(emilon): That's a hack to work around the fact
                        # that removing a torrent is racy.
                        self.utility.session.lm.threadpool.call(
                            0.5, reactor.callInThread,
                            self.utility.session.start_download, tdef, dscfg)

            self.prevActiveDownloads = newActiveDownloads
            if doCheckpoint:
                self.utility.session.checkpoint()

            if self.utility.read_config(u'seeding_mode') == 'never':
                for data in seeding_download_list:
                    data[u'download'].stop()
                    from Tribler.Main.vwxGUI.UserDownloadChoice import UserDownloadChoice
                    UserDownloadChoice.get_singleton().set_download_state(
                        data[u'infohash'], "stop")

            # Adjust speeds and call TunnelCommunity.monitor_downloads once every 4 seconds
            adjustspeeds = False
            if self.ratestatecallbackcount % 4 == 0:
                adjustspeeds = True

            if adjustspeeds and self.tunnel_community:
                self.tunnel_community.monitor_downloads(dslist)

        except:
            print_exc()

        return 1.0, wantpeers

    def loadSessionCheckpoint(self):
        pstate_dir = self.utility.session.get_downloads_pstate_dir()

        filelist = os.listdir(pstate_dir)
        if any([filename.endswith('.pickle') for filename in filelist]):
            convertDownloadCheckpoints(pstate_dir)

        from Tribler.Main.vwxGUI.UserDownloadChoice import UserDownloadChoice
        user_download_choice = UserDownloadChoice.get_singleton()
        initialdlstatus_dict = {}
        for infohash, state in user_download_choice.get_download_states(
        ).iteritems():
            if state == 'stop':
                initialdlstatus_dict[infohash] = DLSTATUS_STOPPED

        self.utility.session.load_checkpoint(
            initialdlstatus_dict=initialdlstatus_dict)

    def guiservthread_free_space_check(self):
        if not (self and self.frame and self.frame.SRstatusbar):
            return

        free_space = get_free_space(
            DefaultDownloadStartupConfig.getInstance().get_dest_dir())
        self.frame.SRstatusbar.RefreshFreeSpace(free_space)

        storage_locations = defaultdict(list)
        for download in self.utility.session.get_downloads():
            if download.get_status() == DLSTATUS_DOWNLOADING:
                storage_locations[download.get_dest_dir()].append(download)

        show_message = False
        low_on_space = [
            path for path in storage_locations.keys() if 0 < get_free_space(
                path) < self.utility.read_config('free_space_threshold')
        ]
        for path in low_on_space:
            for download in storage_locations[path]:
                download.stop()
                show_message = True

        if show_message:
            wx.CallAfter(
                wx.MessageBox,
                "Tribler has detected low disk space. Related downloads have been stopped.",
                "Error")

        self.utility.session.lm.threadpool.call_in_thread(
            FREE_SPACE_CHECK_INTERVAL, self.guiservthread_free_space_check)

    def guiservthread_checkpoint_timer(self):
        """ Periodically checkpoint Session """
        if self.done:
            return
        try:
            self._logger.info("main: Checkpointing Session")
            self.utility.session.checkpoint()

            self.utility.session.lm.threadpool.call_in_thread(
                SESSION_CHECKPOINT_INTERVAL,
                self.guiservthread_checkpoint_timer)
        except:
            print_exc()

    @forceWxThread
    def sesscb_ntfy_activities(self, events):
        if self._frame_and_ready():
            for args in events:
                objectID = args[2]
                args = args[3:]

                self.frame.setActivity(objectID, *args)

    @forceWxThread
    def sesscb_ntfy_reachable(self, subject, changeType, objectID, msg):
        if self._frame_and_ready():
            self.frame.SRstatusbar.onReachable()

    @forceWxThread
    def sesscb_ntfy_channelupdates(self, events):
        if self._frame_and_ready():
            for args in events:
                subject = args[0]
                changeType = args[1]
                objectID = args[2]

                if self.frame.channellist:
                    if len(args) > 3:
                        myvote = args[3]
                    else:
                        myvote = False

                    manager = self.frame.channellist.GetManager()
                    manager.channelUpdated(objectID,
                                           subject == NTFY_VOTECAST,
                                           myvote=myvote)

                manager = self.frame.selectedchannellist.GetManager()
                manager.channelUpdated(objectID,
                                       stateChanged=changeType == NTFY_STATE,
                                       modified=changeType == NTFY_MODIFIED)

                if changeType == NTFY_CREATE:
                    if self.frame.channellist:
                        self.frame.channellist.SetMyChannelId(objectID)

                    self.torrentfeed.register(self.utility.session, objectID)
                    self.torrentfeed.addCallback(
                        objectID, self.guiUtility.channelsearch_manager.
                        createTorrentFromDef)

                self.frame.managechannel.channelUpdated(
                    objectID,
                    created=changeType == NTFY_CREATE,
                    modified=changeType == NTFY_MODIFIED)

    @forceWxThread
    def sesscb_ntfy_torrentupdates(self, events):
        if self._frame_and_ready():
            infohashes = [args[2] for args in events]

            if self.frame.searchlist:
                manager = self.frame.searchlist.GetManager()
                manager.torrentsUpdated(infohashes)

                manager = self.frame.selectedchannellist.GetManager()
                manager.torrentsUpdated(infohashes)

                manager = self.frame.playlist.GetManager()
                manager.torrentsUpdated(infohashes)

                manager = self.frame.librarylist.GetManager()
                manager.torrentsUpdated(infohashes)

            from Tribler.Main.Utility.GuiDBTuples import CollectedTorrent

            if self.frame.torrentdetailspanel.torrent and self.frame.torrentdetailspanel.torrent.infohash in infohashes:
                # If an updated torrent is being shown in the detailspanel, make sure the information gets refreshed.
                t = self.frame.torrentdetailspanel.torrent
                torrent = t.torrent if isinstance(t, CollectedTorrent) else t
                self.frame.torrentdetailspanel.setTorrent(torrent)

            if self.frame.librarydetailspanel.torrent and self.frame.librarydetailspanel.torrent.infohash in infohashes:
                t = self.frame.librarydetailspanel.torrent
                torrent = t.torrent if isinstance(t, CollectedTorrent) else t
                self.frame.librarydetailspanel.setTorrent(torrent)

    def sesscb_ntfy_torrentfinished(self, subject, changeType, objectID,
                                    *args):
        self.guiUtility.Notify(
            "Download Completed",
            "Torrent '%s' has finished downloading. Now seeding." % args[0],
            icon='seed')

        if self._frame_and_ready():
            infohash = objectID
            torrent = self.guiUtility.torrentsearch_manager.getTorrentByInfohash(
                infohash)
            self.guiUtility.library_manager.addDownloadState(torrent)

    def sesscb_ntfy_magnet(self, subject, changetype, objectID, *args):
        if changetype == NTFY_MAGNET_STARTED:
            self.guiUtility.library_manager.magnet_started(objectID)
        elif changetype == NTFY_MAGNET_GOT_PEERS:
            self.guiUtility.library_manager.magnet_got_peers(objectID, args[0])
        elif changetype == NTFY_MAGNET_CLOSE:
            self.guiUtility.library_manager.magnet_close(objectID)

    @forceWxThread
    def sesscb_ntfy_playlistupdates(self, subject, changeType, objectID,
                                    *args):
        if self._frame_and_ready():
            if changeType == NTFY_INSERT:
                self.frame.managechannel.playlistCreated(objectID)

                manager = self.frame.selectedchannellist.GetManager()
                manager.playlistCreated(objectID)

            else:
                self.frame.managechannel.playlistUpdated(
                    objectID, modified=changeType == NTFY_MODIFIED)

                if len(args) > 0:
                    infohash = args[0]
                else:
                    infohash = False
                manager = self.frame.selectedchannellist.GetManager()
                manager.playlistUpdated(objectID,
                                        infohash,
                                        modified=changeType == NTFY_MODIFIED)

                manager = self.frame.playlist.GetManager()
                manager.playlistUpdated(objectID,
                                        modified=changeType == NTFY_MODIFIED)

    @forceWxThread
    def sesscb_ntfy_commentupdates(self, subject, changeType, objectID, *args):
        if self._frame_and_ready():
            self.frame.selectedchannellist.OnCommentCreated(objectID)
            self.frame.playlist.OnCommentCreated(objectID)

    @forceWxThread
    def sesscb_ntfy_modificationupdates(self, subject, changeType, objectID,
                                        *args):
        if self._frame_and_ready():
            self.frame.selectedchannellist.OnModificationCreated(objectID)
            self.frame.playlist.OnModificationCreated(objectID)

    @forceWxThread
    def sesscb_ntfy_moderationupdats(self, subject, changeType, objectID,
                                     *args):
        if self._frame_and_ready():
            self.frame.selectedchannellist.OnModerationCreated(objectID)
            self.frame.playlist.OnModerationCreated(objectID)

    @forceWxThread
    def sesscb_ntfy_markingupdates(self, subject, changeType, objectID, *args):
        if self._frame_and_ready():
            self.frame.selectedchannellist.OnMarkingCreated(objectID)
            self.frame.playlist.OnModerationCreated(objectID)

    @forceWxThread
    def onError(self, e):
        print_exc()
        _, value, stack = sys.exc_info()
        backtrace = traceback.format_exception(type, value, stack)

        win = FeedbackWindow(
            "Unfortunately, Tribler ran into an internal error")
        win.CreateOutputWindow('')
        for line in backtrace:
            win.write(line)

        win.ShowModal()

    @forceWxThread
    def OnExit(self):
        bm = self.gui_image_manager.getImage(u'closescreen.png')
        self.closewindow = GaugeSplash(bm, "Closing...", 6)
        self.closewindow.Show()

        self._logger.info("main: ONEXIT")
        self.ready = False
        self.done = True

        # write all persistent data to disk
        self.closewindow.tick('Write all persistent data to disk')
        if self.torrentfeed:
            self.torrentfeed.shutdown()
            self.torrentfeed.delInstance()
        if self.webUI:
            self.webUI.stop()
            self.webUI.delInstance()

        if self.frame:
            self.frame.Destroy()
            self.frame = None

        # Don't checkpoint, interferes with current way of saving Preferences,
        # see Tribler/Main/Dialogs/abcoption.py
        if self.utility:
            # Niels: lets add a max waiting time for this session shutdown.
            session_shutdown_start = time()

            try:
                self._logger.info("ONEXIT cleaning database")
                self.closewindow.tick('Cleaning database')
                torrent_db = self.utility.session.open_dbhandler(NTFY_TORRENTS)
                torrent_db._db.clean_db(randint(0, 24) == 0, exiting=True)
            except:
                print_exc()

            self.closewindow.tick('Shutdown session')
            self.utility.session.shutdown(hacksessconfcheckpoint=False)

            # Arno, 2012-07-12: Shutdown should be quick
            # Niels, 2013-03-21: However, setting it too low will prevent checkpoints from being written to disk
            waittime = 60
            while not self.utility.session.has_shutdown():
                diff = time() - session_shutdown_start
                if diff > waittime:
                    self._logger.info(
                        "main: ONEXIT NOT Waiting for Session to shutdown, took too long"
                    )
                    break

                self._logger.info(
                    "ONEXIT Waiting for Session to shutdown, will wait for an additional %d seconds",
                    waittime - diff)
                sleep(3)
            self._logger.info("ONEXIT Session is shutdown")

        self.closewindow.tick('Deleting instances')
        self._logger.debug("ONEXIT deleting instances")

        Session.del_instance()
        GUIUtility.delInstance()
        GUIDBProducer.delInstance()
        DefaultDownloadStartupConfig.delInstance()
        GuiImageManager.delInstance()

        self.closewindow.tick('Exiting now')

        self.closewindow.Destroy()

        return 0

    def db_exception_handler(self, e):
        self._logger.debug("Database Exception handler called %s value %s #",
                           e, e.args)
        try:
            if e.args[1] == "DB object has been closed":
                return  # We caused this non-fatal error, don't show.
            if self.error is not None and self.error.args[1] == e.args[1]:
                return  # don't repeat same error
        except:
            self._logger.error("db_exception_handler error %s %s", e, type(e))
            print_exc()
            # print_stack()

        self.onError(e)

    def getConfigPath(self):
        return self.utility.getConfigPath()

    def startWithRightView(self):
        if self.params[0] != "":
            self.guiUtility.ShowPage('my_files')

    def i2ithread_readlinecallback(self, ic, cmd):
        """ Called by Instance2Instance thread """

        self._logger.info("main: Another instance called us with cmd %s", cmd)
        ic.close()

        if cmd.startswith('START '):
            param = cmd[len('START '):].strip().decode("utf-8")
            torrentfilename = None
            if param.startswith('http:'):
                # Retrieve from web
                f = tempfile.NamedTemporaryFile()
                n = urllib2.urlopen(param)
                data = n.read()
                f.write(data)
                f.close()
                n.close()
                torrentfilename = f.name
            else:
                torrentfilename = param

            # Switch to GUI thread
            # New for 5.0: Start in VOD mode
            def start_asked_download():
                if torrentfilename.startswith("magnet:"):
                    self.frame.startDownloadFromMagnet(torrentfilename)
                else:
                    self.frame.startDownload(torrentfilename)
                self.guiUtility.ShowPage('my_files')

            wx.CallAfter(start_asked_download)
Exemplo n.º 3
0
class ABCApp(TaskManager):
    def __init__(self,
                 params,
                 installdir,
                 autoload_discovery=True,
                 use_torrent_search=True,
                 use_channel_search=True):
        super(ABCApp, self).__init__()
        assert not isInIOThread(
        ), "isInIOThread() seems to not be working correctly"
        self._logger = logging.getLogger(self.__class__.__name__)

        self.params = params
        self.installdir = installdir

        self.state_dir = None
        self.error = None
        self.last_update = 0
        self.ready = False
        self.done = False
        self.frame = None
        self.upgrader = None
        self.i2i_server = None

        # DISPERSY will be set when available
        self.dispersy = None
        self.tunnel_community = None

        self.webUI = None
        self.utility = None

        # Stage 1 start
        session = self.InitStage1(installdir,
                                  autoload_discovery=autoload_discovery,
                                  use_torrent_search=use_torrent_search,
                                  use_channel_search=use_channel_search)

        try:
            self._logger.info('Client Starting Up.')
            self._logger.info("Tribler is using %s as working directory",
                              self.installdir)

            # Stage 2: show the splash window and start the session

            self.utility = Utility(self.installdir, session.get_state_dir())

            if self.utility.read_config(u'saveas', u'downloadconfig'):
                DefaultDownloadStartupConfig.getInstance().set_dest_dir(
                    self.utility.read_config(u'saveas', u'downloadconfig'))

            self.utility.set_app(self)
            self.utility.set_session(session)
            self.guiUtility = GUIUtility.getInstance(self.utility, self.params,
                                                     self)
            GUIDBProducer.getInstance()

            # Broadcast that the initialisation is starting for the splash gauge and those who are interested
            self.utility.session.notifier.notify(NTFY_STARTUP_TICK,
                                                 NTFY_CREATE, None, None)

            session.notifier.notify(NTFY_STARTUP_TICK, NTFY_INSERT, None,
                                    'Starting API')
            wx.Yield()

            self._logger.info('Tribler Version: %s Build: %s', version_id,
                              commit_id)

            version_info = self.utility.read_config('version_info')
            if version_info.get('version_id', None) != version_id:
                # First run of a different version
                version_info['first_run'] = int(time())
                version_info['version_id'] = version_id
                self.utility.write_config('version_info', version_info)

            session.notifier.notify(
                NTFY_STARTUP_TICK, NTFY_INSERT, None,
                'Starting session and upgrading database (it may take a while)'
            )
            wx.Yield()

            session.start()
            self.dispersy = session.lm.dispersy
            self.dispersy.attach_progress_handler(self.progressHandler)

            session.notifier.notify(NTFY_STARTUP_TICK, NTFY_INSERT, None,
                                    'Initializing Family Filter')
            wx.Yield()
            cat = session.lm.category

            state = self.utility.read_config('family_filter')
            if state in (1, 0):
                cat.set_family_filter(state == 1)
            else:
                self.utility.write_config('family_filter', 1)
                self.utility.flush_config()

                cat.set_family_filter(True)

            # Create global speed limits
            session.notifier.notify(NTFY_STARTUP_TICK, NTFY_INSERT, None,
                                    'Setting up speed limits')
            wx.Yield()

            # Counter to suppress some event from occurring
            self.ratestatecallbackcount = 0

            maxup = self.utility.read_config('maxuploadrate')
            maxdown = self.utility.read_config('maxdownloadrate')
            # set speed limits using LibtorrentMgr
            session.set_max_upload_speed(maxup)
            session.set_max_download_speed(maxdown)

            # Only allow updates to come in after we defined ratelimiter
            self.prevActiveDownloads = []
            session.set_download_states_callback(self.sesscb_states_callback)

            # Schedule task for checkpointing Session, to avoid hash checks after
            # crashes.
            self.register_task("checkpoint_loop", LoopingCall(self.guiservthread_checkpoint_timer))\
                .start(SESSION_CHECKPOINT_INTERVAL, now=False)

            session.notifier.notify(NTFY_STARTUP_TICK, NTFY_INSERT, None,
                                    'GUIUtility register')
            wx.Yield()
            self.guiUtility.register()

            self.frame = MainFrame(self, None, False)
            self.frame.SetIcon(
                wx.Icon(
                    os.path.join(self.installdir, 'Tribler', 'Main', 'vwxGUI',
                                 'images', 'tribler.ico'), wx.BITMAP_TYPE_ICO))

            # Arno, 2011-06-15: VLC 1.1.10 pops up separate win, don't have two.
            self.frame.videoframe = None

            if sys.platform == 'win32':
                wx.CallAfter(self.frame.top_bg.Refresh)
                wx.CallAfter(self.frame.top_bg.Layout)
            else:
                self.frame.top_bg.Layout()

            # Arno, 2007-05-03: wxWidgets 2.8.3.0 and earlier have the MIME-type for .bmp
            # files set to 'image/x-bmp' whereas 'image/bmp' is the official one.
            try:
                bmphand = None
                hands = wx.Image.GetHandlers()
                for hand in hands:
                    if hand.GetMimeType() == 'image/x-bmp':
                        bmphand = hand
                        break
                # wx.Image.AddHandler()
                if bmphand is not None:
                    bmphand.SetMimeType('image/bmp')
            except:
                # wx < 2.7 don't like wx.Image.GetHandlers()
                print_exc()

            session.notifier.notify(NTFY_STARTUP_TICK, NTFY_DELETE, None, None)
            wx.Yield()
            self.frame.Show(True)
            self.register_task('free_space_check', LoopingCall(self.guiservthread_free_space_check))\
                .start(FREE_SPACE_CHECK_INTERVAL)

            self.webUI = None
            if self.utility.read_config('use_webui'):
                try:
                    from Tribler.Main.webUI.webUI import WebUI
                    self.webUI = WebUI.getInstance(
                        self.guiUtility.library_manager,
                        self.guiUtility.torrentsearch_manager,
                        self.utility.read_config('webui_port'))
                    self.webUI.start()
                except Exception:
                    print_exc()

            self.emercoin_mgr = None
            try:
                from Tribler.Main.Emercoin.EmercoinMgr import EmercoinMgr
                self.emercoin_mgr = EmercoinMgr(self.utility)
            except Exception:
                print_exc()

            wx.CallAfter(self.PostInit2)

            # 08/02/10 Boudewijn: Working from home though console
            # doesn't allow me to press close.  The statement below
            # gracefully closes Tribler after 120 seconds.
            # wx.CallLater(120*1000, wx.GetApp().Exit)

            self.ready = True

        except Exception as e:
            session.notifier.notify(NTFY_STARTUP_TICK, NTFY_DELETE, None, None)
            self.onError(e)

    def InitStage1(self,
                   installdir,
                   autoload_discovery=True,
                   use_torrent_search=True,
                   use_channel_search=True):
        """ Stage 1 start: pre-start the session to handle upgrade.
        """

        self.gui_image_manager = GuiImageManager.getInstance(installdir)

        # Start Tribler Session
        defaultConfig = SessionStartupConfig()
        state_dir = defaultConfig.get_state_dir()

        # Switch to the state dir so relative paths can be used (IE, in LevelDB store paths)
        if not os.path.exists(state_dir):
            os.makedirs(state_dir)
        os.chdir(state_dir)

        cfgfilename = Session.get_default_config_filename(state_dir)

        self._logger.debug(u"Session config %s", cfgfilename)

        self.sconfig = SessionStartupConfig.load(cfgfilename)
        self.sconfig.set_install_dir(self.installdir)

        if not self.sconfig.get_watch_folder_path():
            default_watch_folder_dir = os.path.join(get_home_dir(),
                                                    u'Downloads',
                                                    u'TriblerWatchFolder')
            self.sconfig.set_watch_folder_path(default_watch_folder_dir)
            if not os.path.exists(default_watch_folder_dir):
                os.makedirs(default_watch_folder_dir)

        # TODO(emilon): Do we still want to force limit this? With the new
        # torrent store it should be pretty fast even with more that that.

        # Arno, 2010-03-31: Hard upgrade to 50000 torrents collected
        self.sconfig.set_torrent_collecting_max_torrents(50000)

        dlcfgfilename = get_default_dscfg_filename(
            self.sconfig.get_state_dir())
        self._logger.debug("main: Download config %s", dlcfgfilename)

        if os.path.exists(dlcfgfilename):
            defaultDLConfig = DefaultDownloadStartupConfig.load(dlcfgfilename)
        else:
            defaultDLConfig = DefaultDownloadStartupConfig.getInstance()

        if not defaultDLConfig.get_dest_dir():
            defaultDLConfig.set_dest_dir(get_default_dest_dir())
        if not os.path.isdir(defaultDLConfig.get_dest_dir()):
            try:
                os.makedirs(defaultDLConfig.get_dest_dir())
            except:
                # Could not create directory, ask user to select a different location
                dlg = wx.DirDialog(
                    None,
                    "Could not find download directory, please select a new location to store your downloads",
                    style=wx.DEFAULT_DIALOG_STYLE)
                dlg.SetPath(get_default_dest_dir())
                if dlg.ShowModal() == wx.ID_OK:
                    new_dest_dir = dlg.GetPath()
                    defaultDLConfig.set_dest_dir(new_dest_dir)
                    defaultDLConfig.save(dlcfgfilename)
                    self.sconfig.save(cfgfilename)
                else:
                    # Quit
                    self.onError = lambda e: self._logger.error(
                        "tribler: quitting due to non-existing destination directory"
                    )
                    raise Exception()

        if not use_torrent_search:
            self.sconfig.set_enable_torrent_search(False)
        if not use_channel_search:
            self.sconfig.set_enable_channel_search(False)

        session = Session(self.sconfig, autoload_discovery=autoload_discovery)
        session.add_observer(self.show_upgrade_dialog, NTFY_UPGRADER,
                             [NTFY_STARTED])

        while not session.upgrader.is_done:
            wx.SafeYield()
            sleep(0.1)

        return session

    @forceWxThread
    def show_upgrade_dialog(self, subject, changetype, objectID, *args):
        assert wx.Thread_IsMain()

        upgrade_dialog = TriblerUpgradeDialog(self.gui_image_manager,
                                              self.upgrader)
        failed = upgrade_dialog.ShowModal()
        upgrade_dialog.Destroy()
        if failed:
            wx.MessageDialog(
                None, "Failed to upgrade the on disk data.\n\n"
                "Tribler has backed up the old data and will now start from scratch.\n\n"
                "Get in contact with the Tribler team if you want to help debugging this issue.\n\n"
                "Error was: %s" % self.upgrader.current_status,
                "Data format upgrade failed",
                wx.OK | wx.CENTRE | wx.ICON_EXCLAMATION).ShowModal()

    def _frame_and_ready(self):
        return self.ready and self.frame and self.frame.ready

    def PostInit2(self):
        self.frame.Raise()
        self.startWithRightView()
        self.set_reputation()

        s = self.utility.session
        s.add_observer(self.sesscb_ntfy_reachable, NTFY_REACHABLE,
                       [NTFY_INSERT])
        s.add_observer(self.sesscb_ntfy_activities,
                       NTFY_ACTIVITIES, [NTFY_INSERT],
                       cache=10)
        s.add_observer(
            self.sesscb_ntfy_channelupdates,
            NTFY_CHANNELCAST,
            [NTFY_INSERT, NTFY_UPDATE, NTFY_CREATE, NTFY_STATE, NTFY_MODIFIED],
            cache=10)
        s.add_observer(self.sesscb_ntfy_channelupdates,
                       NTFY_VOTECAST, [NTFY_UPDATE],
                       cache=10)
        s.add_observer(self.sesscb_ntfy_myprefupdates, NTFY_MYPREFERENCES,
                       [NTFY_INSERT, NTFY_UPDATE, NTFY_DELETE])
        s.add_observer(self.sesscb_ntfy_torrentupdates,
                       NTFY_TORRENTS, [NTFY_UPDATE, NTFY_INSERT],
                       cache=10)
        s.add_observer(self.sesscb_ntfy_playlistupdates, NTFY_PLAYLISTS,
                       [NTFY_INSERT, NTFY_UPDATE])
        s.add_observer(self.sesscb_ntfy_commentupdates, NTFY_COMMENTS,
                       [NTFY_INSERT, NTFY_DELETE])
        s.add_observer(self.sesscb_ntfy_modificationupdates,
                       NTFY_MODIFICATIONS, [NTFY_INSERT])
        s.add_observer(self.sesscb_ntfy_moderationupdats, NTFY_MODERATIONS,
                       [NTFY_INSERT])
        s.add_observer(self.sesscb_ntfy_markingupdates, NTFY_MARKINGS,
                       [NTFY_INSERT])
        s.add_observer(self.sesscb_ntfy_torrentfinished, NTFY_TORRENTS,
                       [NTFY_FINISHED])
        s.add_observer(
            self.sesscb_ntfy_magnet, NTFY_TORRENTS,
            [NTFY_MAGNET_GOT_PEERS, NTFY_MAGNET_STARTED, NTFY_MAGNET_CLOSE])
        s.add_observer(self.sesscb_ntfy_corrupt_torrent,
                       NTFY_WATCH_FOLDER_CORRUPT_TORRENT, [NTFY_INSERT])
        s.add_observer(self.sesscb_ntfy_newversion, NTFY_NEW_VERSION,
                       [NTFY_INSERT])

        # Check for a new version
        s.lm.version_check_manager.start(24 * 3600)

        # TODO(emilon): Use the LogObserver I already implemented
        # self.dispersy.callback.attach_exception_handler(self.frame.exceptionHandler)

    @forceWxThread
    def sesscb_ntfy_myprefupdates(self, subject, changeType, objectID, *args):
        if self._frame_and_ready():
            if changeType in [NTFY_INSERT, NTFY_UPDATE]:
                if changeType == NTFY_INSERT:
                    if self.frame.searchlist:
                        manager = self.frame.searchlist.GetManager()
                        manager.downloadStarted(objectID)

                    manager = self.frame.selectedchannellist.GetManager()
                    manager.downloadStarted(objectID)

                manager = self.frame.librarylist.GetManager()
                manager.downloadStarted(objectID)
            elif changeType == NTFY_DELETE:
                self.guiUtility.frame.librarylist.RemoveItem(objectID)

                if self.guiUtility.frame.librarylist.IsShownOnScreen() and \
                   self.guiUtility.frame.librarydetailspanel.torrent and \
                   self.guiUtility.frame.librarydetailspanel.torrent.infohash == objectID:
                    self.guiUtility.frame.librarylist.ResetBottomWindow()
                    self.guiUtility.frame.top_bg.ClearButtonHandlers()

                if self.guiUtility.frame.librarylist.list.IsEmpty():
                    self.guiUtility.frame.librarylist.SetData([])

    def progressHandler(self, title, message, maximum):
        from Tribler.Main.Dialogs.ThreadSafeProgressDialog import ThreadSafeProgressDialog
        return ThreadSafeProgressDialog(
            title, message, maximum, None, wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME
            | wx.PD_ESTIMATED_TIME | wx.PD_REMAINING_TIME | wx.PD_AUTO_HIDE)

    def set_reputation(self):
        def do_db():
            nr_connections = 0
            nr_channel_connections = 0
            if self.dispersy:
                for community in self.dispersy.get_communities():
                    from Tribler.community.search.community import SearchCommunity
                    from Tribler.community.allchannel.community import AllChannelCommunity

                    if isinstance(community, SearchCommunity):
                        nr_connections = community.get_nr_connections()
                    elif isinstance(community, AllChannelCommunity):
                        nr_channel_connections = community.get_nr_connections()

            return nr_connections, nr_channel_connections

        def do_wx(delayedResult):
            if not self.frame:
                return

            nr_connections, nr_channel_connections = delayedResult.get()

            # self.frame.SRstatusbar.set_reputation(myRep, total_down, total_up)

            # bitmap is 16px wide, -> but first and last pixel do not add anything.
            percentage = min(1.0, (nr_connections + 1) / 16.0)
            self.frame.SRstatusbar.SetConnections(percentage, nr_connections,
                                                  nr_channel_connections)

        """ set the reputation in the GUI"""
        if self._frame_and_ready():
            startWorker(do_wx, do_db, uId=u"tribler.set_reputation")
        startWorker(None,
                    self.set_reputation,
                    delay=5.0,
                    workerType="ThreadPool")

    def sesscb_states_callback(self, dslist):
        if not self.ready:
            return []

        # update tray icon
        total_download, total_upload = get_download_upload_speed(dslist)
        if self.frame and self.frame.tbicon:
            self.frame.tbicon.updateTooltip(total_download, total_upload)

        wantpeers = []
        self.ratestatecallbackcount += 1
        try:
            # Print stats on Console
            if self.ratestatecallbackcount % 5 == 0:
                for ds in dslist:
                    safename = repr(ds.get_download().get_def().get_name())
                    self._logger.debug("%s %s %.1f%% dl %.1f ul %.1f n %d",
                                       safename,
                                       dlstatus_strings[ds.get_status()],
                                       100.0 * ds.get_progress(),
                                       ds.get_current_speed(DOWNLOAD),
                                       ds.get_current_speed(UPLOAD),
                                       ds.get_num_peers())
                    if ds.get_status() == DLSTATUS_STOPPED_ON_ERROR:
                        self._logger.error("main: Error: %s",
                                           repr(ds.get_error()))
                        download = self.utility.session.lm.downloads.get(
                            ds.get_infohash())
                        if download:
                            download.stop()

                        # show error dialog
                        dlg = wx.MessageDialog(
                            self.frame, "Download stopped on error: %s" %
                            repr(ds.get_error()), "Download Error",
                            wx.OK | wx.ICON_ERROR)
                        dlg.ShowModal()
                        dlg.Destroy()

            # Pass DownloadStates to libaryView
            no_collected_list = [ds for ds in dslist]
            try:
                # Arno, 2012-07-17: Retrieving peerlist for the DownloadStates takes CPU
                # so only do it when needed for display.
                wantpeers.extend(
                    self.guiUtility.library_manager.download_state_callback(
                        no_collected_list))
            except:
                print_exc()

            # Check to see if a download has finished
            newActiveDownloads = []
            doCheckpoint = False
            seeding_download_list = []
            for ds in dslist:
                state = ds.get_status()
                download = ds.get_download()
                tdef = download.get_def()
                safename = tdef.get_name_as_unicode()

                if state == DLSTATUS_DOWNLOADING:
                    newActiveDownloads.append(safename)

                elif state == DLSTATUS_SEEDING:
                    seeding_download_list.append({
                        u'infohash':
                        tdef.get_infohash(),
                        u'download':
                        download,
                    })

                    if safename in self.prevActiveDownloads:
                        infohash = tdef.get_infohash()

                        self.utility.session.notifier.notify(
                            NTFY_TORRENTS, NTFY_FINISHED, infohash, safename)

                        doCheckpoint = True

                    elif download.get_hops(
                    ) == 0 and download.get_safe_seeding():
                        hops = self.utility.read_config('default_number_hops')
                        self._logger.info(
                            "Moving completed torrent to tunneled session %d for hidden sedding %r",
                            hops, download)
                        self.utility.session.remove_download(download)

                        # copy the old download_config and change the hop count
                        dscfg = download.copy()
                        dscfg.set_hops(hops)

                        # TODO(emilon): That's a hack to work around the fact
                        # that removing a torrent is racy.
                        def schedule_download():
                            self.register_task(
                                "reschedule_dowload",
                                reactor.callLater(
                                    5, reactor.callInThread, self.utility.
                                    session.start_download_from_tdef, tdef,
                                    dscfg))

                        reactor.callFromThread(schedule_download)

            self.prevActiveDownloads = newActiveDownloads
            if doCheckpoint:
                self.utility.session.checkpoint()

            if self.utility.read_config(u'seeding_mode') == 'never':
                for data in seeding_download_list:
                    data[u'download'].stop()
                    self.utility.session.tribler_config.set_download_state(
                        data[u'infohash'], "stop")

            # Adjust speeds and call TunnelCommunity.monitor_downloads once every 4 seconds
            adjustspeeds = False
            if self.ratestatecallbackcount % 4 == 0:
                adjustspeeds = True

            if adjustspeeds and self.tunnel_community:
                self.tunnel_community.monitor_downloads(dslist)

        except:
            print_exc()

        return wantpeers

    @forceWxThread
    def guiservthread_free_space_check(self):
        free_space = get_free_space(
            DefaultDownloadStartupConfig.getInstance().get_dest_dir())
        self.frame.SRstatusbar.RefreshFreeSpace(free_space)

        storage_locations = defaultdict(list)
        for download in self.utility.session.get_downloads():
            if download.get_status() == DLSTATUS_DOWNLOADING:
                storage_locations[download.get_dest_dir()].append(download)

        show_message = False
        low_on_space = [
            path for path in storage_locations.keys() if 0 < get_free_space(
                path) < self.utility.read_config('free_space_threshold')
        ]
        for path in low_on_space:
            for download in storage_locations[path]:
                download.stop()
                show_message = True

        if show_message:
            wx.CallAfter(
                wx.MessageBox,
                "Tribler has detected low disk space. Related downloads have been stopped.",
                "Error")

    def guiservthread_checkpoint_timer(self):
        """ Periodically checkpoint Session """
        self._logger.info("main: Checkpointing Session")
        return deferToThread(self.utility.session.checkpoint)

    @forceWxThread
    def sesscb_ntfy_activities(self, events):
        if self._frame_and_ready():
            for args in events:
                objectID = args[2]
                args = args[3:]

                self.frame.setActivity(objectID, *args)

    @forceWxThread
    def sesscb_ntfy_reachable(self, subject, changeType, objectID, msg):
        if self._frame_and_ready():
            self.frame.SRstatusbar.onReachable()

    @forceWxThread
    def sesscb_ntfy_channelupdates(self, events):
        if self._frame_and_ready():
            for args in events:
                subject = args[0]
                changeType = args[1]
                objectID = args[2]

                if self.frame.channellist:
                    if len(args) > 3:
                        myvote = args[3]
                    else:
                        myvote = False

                    manager = self.frame.channellist.GetManager()
                    manager.channelUpdated(objectID,
                                           subject == NTFY_VOTECAST,
                                           myvote=myvote)

                manager = self.frame.selectedchannellist.GetManager()
                manager.channelUpdated(objectID,
                                       stateChanged=changeType == NTFY_STATE,
                                       modified=changeType == NTFY_MODIFIED)

                if changeType == NTFY_CREATE:
                    if self.frame.channellist:
                        self.frame.channellist.SetMyChannelId(objectID)

                self.frame.managechannel.channelUpdated(
                    objectID,
                    created=changeType == NTFY_CREATE,
                    modified=changeType == NTFY_MODIFIED)

    @forceWxThread
    def sesscb_ntfy_torrentupdates(self, events):
        if self._frame_and_ready():
            infohashes = [args[2] for args in events]

            if self.frame.searchlist:
                manager = self.frame.searchlist.GetManager()
                manager.torrentsUpdated(infohashes)

                manager = self.frame.selectedchannellist.GetManager()
                manager.torrentsUpdated(infohashes)

                manager = self.frame.playlist.GetManager()
                manager.torrentsUpdated(infohashes)

                manager = self.frame.librarylist.GetManager()
                manager.torrentsUpdated(infohashes)

                if self.utility.session.get_creditmining_enable():
                    manager = self.frame.creditminingpanel.cmlist.GetManager()
                    manager.torrents_updated(infohashes)

            from Tribler.Main.Utility.GuiDBTuples import CollectedTorrent

            if self.frame.torrentdetailspanel.torrent and self.frame.torrentdetailspanel.torrent.infohash in infohashes:
                # If an updated torrent is being shown in the detailspanel, make sure the information gets refreshed.
                t = self.frame.torrentdetailspanel.torrent
                torrent = t.torrent if isinstance(t, CollectedTorrent) else t
                self.frame.torrentdetailspanel.setTorrent(torrent)

            if self.frame.librarydetailspanel.torrent and self.frame.librarydetailspanel.torrent.infohash in infohashes:
                t = self.frame.librarydetailspanel.torrent
                torrent = t.torrent if isinstance(t, CollectedTorrent) else t
                self.frame.librarydetailspanel.setTorrent(torrent)

    def sesscb_ntfy_torrentfinished(self, subject, changeType, objectID,
                                    *args):
        self.guiUtility.Notify(
            "Download Completed",
            "Torrent '%s' has finished downloading. Now seeding." % args[0],
            icon='seed')

        if self._frame_and_ready():
            infohash = objectID
            torrent = self.guiUtility.torrentsearch_manager.getTorrentByInfohash(
                infohash)
            # Check if we got the actual torrent as the bandwidth investor
            # downloads aren't going to be there.
            if torrent:
                self.guiUtility.library_manager.addDownloadState(torrent)

    @forceWxThread
    def sesscb_ntfy_newversion(self, subject, changeType, objectID, *args):
        if str(self.utility.read_config('last_reported_version')) == args[0]:
            return

        new_version_dialog = NewVersionDialog(
            args[0],
            self.frame,
            'new_version_dialog',
            'New version available',
            title='New version',
            msg="Version %s of Tribler is available. "
            "Do you want to visit the website to download the newest version?"
            % args[0])
        new_version_dialog.ShowModal()
        new_version_dialog.Destroy()

    @forceWxThread
    def sesscb_ntfy_magnet(self, subject, changetype, objectID, *args):
        if changetype == NTFY_MAGNET_STARTED:
            self.guiUtility.library_manager.magnet_started(objectID)
        elif changetype == NTFY_MAGNET_GOT_PEERS:
            self.guiUtility.library_manager.magnet_got_peers(objectID, args[0])
        elif changetype == NTFY_MAGNET_CLOSE:
            self.guiUtility.library_manager.magnet_close(objectID)

    @forceWxThread
    def sesscb_ntfy_corrupt_torrent(self, subject, changetype, objectID,
                                    *args):
        dlg = wx.MessageDialog(
            self.frame,
            "Unable to add corrupt torrent in watch folder to downloads: %s" %
            args[0], "Corrupt torrent", wx.OK | wx.ICON_ERROR)
        dlg.ShowModal()
        dlg.Destroy()

    @forceWxThread
    def sesscb_ntfy_playlistupdates(self, subject, changeType, objectID,
                                    *args):
        if self._frame_and_ready():
            if changeType == NTFY_INSERT:
                self.frame.managechannel.playlistCreated(objectID)

                manager = self.frame.selectedchannellist.GetManager()
                manager.playlistCreated(objectID)

            else:
                self.frame.managechannel.playlistUpdated(
                    objectID, modified=changeType == NTFY_MODIFIED)

                if len(args) > 0:
                    infohash = args[0]
                else:
                    infohash = False
                manager = self.frame.selectedchannellist.GetManager()
                manager.playlistUpdated(objectID,
                                        infohash,
                                        modified=changeType == NTFY_MODIFIED)

                manager = self.frame.playlist.GetManager()
                manager.playlistUpdated(objectID,
                                        modified=changeType == NTFY_MODIFIED)

    @forceWxThread
    def sesscb_ntfy_commentupdates(self, subject, changeType, objectID, *args):
        if self._frame_and_ready():
            self.frame.selectedchannellist.OnCommentCreated(objectID)
            self.frame.playlist.OnCommentCreated(objectID)

    @forceWxThread
    def sesscb_ntfy_modificationupdates(self, subject, changeType, objectID,
                                        *args):
        if self._frame_and_ready():
            self.frame.selectedchannellist.OnModificationCreated(objectID)
            self.frame.playlist.OnModificationCreated(objectID)

    @forceWxThread
    def sesscb_ntfy_moderationupdats(self, subject, changeType, objectID,
                                     *args):
        if self._frame_and_ready():
            self.frame.selectedchannellist.OnModerationCreated(objectID)
            self.frame.playlist.OnModerationCreated(objectID)

    @forceWxThread
    def sesscb_ntfy_markingupdates(self, subject, changeType, objectID, *args):
        if self._frame_and_ready():
            self.frame.selectedchannellist.OnMarkingCreated(objectID)
            self.frame.playlist.OnModerationCreated(objectID)

    @forceWxThread
    def onError(self, e):
        print_exc()
        _, value, stack = sys.exc_info()
        backtrace = traceback.format_exception(type, value, stack)

        win = FeedbackWindow(
            "Unfortunately, Tribler ran into an internal error")
        win.CreateOutputWindow('')
        for line in backtrace:
            win.write(line)

        win.ShowModal()

    @forceWxThread
    def OnExit(self):
        self.utility.session.notifier.notify(NTFY_CLOSE_TICK, NTFY_CREATE,
                                             None, None)

        blockingCallFromThread(reactor, self.cancel_all_pending_tasks)

        if self.i2i_server:
            self.i2i_server.stop()

        self._logger.info("main: ONEXIT")
        self.ready = False
        self.done = True

        # write all persistent data to disk
        self.utility.session.notifier.notify(
            NTFY_CLOSE_TICK, NTFY_INSERT, None,
            'Write all persistent data to disk')
        wx.Yield()

        if self.webUI:
            self.webUI.stop()
            self.webUI.delInstance()

        if self.frame:
            self.frame.Destroy()
            self.frame = None

        # Don't checkpoint, interferes with current way of saving Preferences,
        # see Tribler/Main/Dialogs/abcoption.py
        if self.utility:
            # Niels: lets add a max waiting time for this session shutdown.
            session_shutdown_start = time()

            # TODO(emilon): probably more notification callbacks should be remmoved
            # here
            s = self.utility.session
            s.remove_observer(self.sesscb_ntfy_newversion)
            s.remove_observer(self.sesscb_ntfy_corrupt_torrent)
            s.remove_observer(self.sesscb_ntfy_magnet)
            s.remove_observer(self.sesscb_ntfy_torrentfinished)
            s.remove_observer(self.sesscb_ntfy_markingupdates)
            s.remove_observer(self.sesscb_ntfy_moderationupdats)
            s.remove_observer(self.sesscb_ntfy_modificationupdates)
            s.remove_observer(self.sesscb_ntfy_commentupdates)
            s.remove_observer(self.sesscb_ntfy_playlistupdates)
            s.remove_observer(self.sesscb_ntfy_torrentupdates)
            s.remove_observer(self.sesscb_ntfy_myprefupdates)
            s.remove_observer(self.sesscb_ntfy_channelupdates)
            s.remove_observer(self.sesscb_ntfy_channelupdates)
            s.remove_observer(self.sesscb_ntfy_activities)
            s.remove_observer(self.sesscb_ntfy_reachable)

            try:
                self._logger.info("ONEXIT cleaning database")
                self.utility.session.notifier.notify(NTFY_CLOSE_TICK,
                                                     NTFY_INSERT, None,
                                                     'Cleaning database')
                wx.Yield()
                torrent_db = self.utility.session.open_dbhandler(NTFY_TORRENTS)
                torrent_db._db.clean_db(randint(0, 24) == 0, exiting=True)
            except:
                print_exc()

            self.utility.session.notifier.notify(NTFY_CLOSE_TICK, NTFY_INSERT,
                                                 None, 'Shutdown session')
            wx.Yield()
            self.utility.session.shutdown(hacksessconfcheckpoint=False)

            # Arno, 2012-07-12: Shutdown should be quick
            # Niels, 2013-03-21: However, setting it too low will prevent checkpoints from being written to disk
            waittime = 60
            while not self.utility.session.has_shutdown():
                diff = time() - session_shutdown_start
                if diff > waittime:
                    self._logger.info(
                        "main: ONEXIT NOT Waiting for Session to shutdown, took too long"
                    )
                    break

                self._logger.info(
                    "ONEXIT Waiting for Session to shutdown, will wait for an additional %d seconds",
                    waittime - diff)
                sleep(3)
            self._logger.info("ONEXIT Session is shutdown")

        self.utility.session.notifier.notify(NTFY_CLOSE_TICK, NTFY_INSERT,
                                             None, 'Deleting instances')
        self._logger.debug("ONEXIT deleting instances")

        Session.del_instance()
        GUIDBProducer.delInstance()
        DefaultDownloadStartupConfig.delInstance()
        GuiImageManager.delInstance()

        self.utility.session.notifier.notify(NTFY_CLOSE_TICK, NTFY_INSERT,
                                             None, 'Exiting now')

        self.utility.session.notifier.notify(NTFY_CLOSE_TICK, NTFY_DELETE,
                                             None, None)

        GUIUtility.delInstance()

    def db_exception_handler(self, e):
        self._logger.debug("Database Exception handler called %s value %s #",
                           e, e.args)
        try:
            if e.args[1] == "DB object has been closed":
                return  # We caused this non-fatal error, don't show.
            if self.error is not None and self.error.args[1] == e.args[1]:
                return  # don't repeat same error
        except:
            self._logger.error("db_exception_handler error %s %s", e, type(e))
            print_exc()
            # print_stack()

        self.onError(e)

    def getConfigPath(self):
        return self.utility.getConfigPath()

    def startWithRightView(self):
        if self.params[0] != "":
            self.guiUtility.ShowPage('my_files')
Exemplo n.º 4
0
class ABCApp():

    def __init__(self, params, single_instance_checker, installdir):
        self.params = params
        self.single_instance_checker = single_instance_checker
        self.installdir = self.configure_install_dir(installdir)

        self.state_dir = None
        self.error = None
        self.last_update = 0
        self.ready = False
        self.done = False
        self.frame = None

        self.guiserver = GUITaskQueue.getInstance()
        self.said_start_playback = False
        self.decodeprogress = 0

        self.old_reputation = 0

        # DISPERSY will be set when available
        self.dispersy = None
        # BARTER_COMMUNITY will be set when both Dispersy and the EffortCommunity are available
        self.barter_community = None

        self.seedingmanager = None
        self.i2is = None
        self.torrentfeed = None
        self.webUI = None
        self.utility = None
        self.videoplayer = None

        try:
            bm = wx.Bitmap(os.path.join(self.installdir, 'Tribler', 'Main', 'vwxGUI', 'images', 'splash.png'), wx.BITMAP_TYPE_ANY)
            self.splash = GaugeSplash(bm)
            self.splash.setTicks(10)
            self.splash.Show()

            print >> sys.stderr, 'Client Starting Up.'
            print >> sys.stderr, "Tribler is using", self.installdir, "as working directory"

            self.splash.tick('Starting API')
            s = self.startAPI(self.splash.tick)

            print >> sys.stderr, "Tribler is expecting swift in", self.sconfig.get_swift_path()

            self.dispersy = s.lm.dispersy

            self.utility = Utility(self.installdir, s.get_state_dir())
            self.utility.app = self
            self.utility.session = s
            self.guiUtility = GUIUtility.getInstance(self.utility, self.params, self)
            GUIDBProducer.getInstance(self.dispersy.callback)

            print >> sys.stderr, 'Tribler Version:', self.utility.lang.get('version'), ' Build:', self.utility.lang.get('build')

            self.splash.tick('Loading userdownloadchoice')
            from Tribler.Main.vwxGUI.UserDownloadChoice import UserDownloadChoice
            UserDownloadChoice.get_singleton().set_session_dir(s.get_state_dir())

            self.splash.tick('Initializing Family Filter')
            cat = Category.getInstance()

            state = self.utility.config.Read('family_filter')
            if state in ('1', '0'):
                cat.set_family_filter(state == '1')
            else:
                self.utility.config.Write('family_filter', '1')
                self.utility.config.Flush()

                cat.set_family_filter(True)

            # Create global rate limiter
            self.splash.tick('Setting up ratelimiters')
            self.ratelimiter = UserDefinedMaxAlwaysOtherwiseDividedOverActiveSwarmsRateManager()

            # Counter to suppress some event from occurring
            self.ratestatecallbackcount = 0

            # So we know if we asked for peer details last cycle
            self.lastwantpeers = []

            # boudewijn 01/04/2010: hack to fix the seedupload speed that
            # was never used and defaulted to 0 (unlimited upload)
            maxup = self.utility.config.Read('maxuploadrate', "int")
            if maxup == -1:  # no upload
                self.ratelimiter.set_global_max_speed(UPLOAD, 0.00001)
                self.ratelimiter.set_global_max_seedupload_speed(0.00001)
            else:
                self.ratelimiter.set_global_max_speed(UPLOAD, maxup)
                self.ratelimiter.set_global_max_seedupload_speed(maxup)

            maxdown = self.utility.config.Read('maxdownloadrate', "int")
            self.ratelimiter.set_global_max_speed(DOWNLOAD, maxdown)

            self.seedingmanager = GlobalSeedingManager(self.utility.config.Read)

            # Only allow updates to come in after we defined ratelimiter
            self.prevActiveDownloads = []
            s.set_download_states_callback(self.sesscb_states_callback)

            # Schedule task for checkpointing Session, to avoid hash checks after
            # crashes.
            self.guiserver.add_task(self.guiservthread_checkpoint_timer, SESSION_CHECKPOINT_INTERVAL)

            self.utility.postAppInit(os.path.join(self.installdir, 'Tribler', 'Main', 'vwxGUI', 'images', 'tribler.ico'))

            # Put it here so an error is shown in the startup-error popup
            # Start server for instance2instance communication
            self.i2iconnhandler = InstanceConnectionHandler(self.i2ithread_readlinecallback)
            self.i2is = Instance2InstanceServer(I2I_LISTENPORT, self.i2iconnhandler)
            self.i2is.start()

            # Arno, 2010-01-15: VLC's reading behaviour of doing open-ended
            # Range: GETs causes performance problems in our code. Disable for now.
            # Arno, 2010-01-22: With the addition of a CachingStream the problem
            # is less severe (see VideoPlayer), so keep GET Range enabled.
            #
            # SimpleServer.RANGE_REQUESTS_ENABLED = False

            # Fire up the VideoPlayer, it abstracts away whether we're using
            # an internal or external video player.
            playbackmode = self.utility.config.Read('videoplaybackmode', "int")
            self.videoplayer = VideoPlayer.getInstance(httpport=VIDEOHTTP_LISTENPORT)
            self.videoplayer.register(self.utility, preferredplaybackmode=playbackmode)

            notification_init(self.utility)
            self.guiUtility.register()

            channel_only = os.path.exists(os.path.join(self.installdir, 'joinchannel'))
            if channel_only:
                f = open(os.path.join(self.installdir, 'joinchannel'), 'rb')
                channel_only = f.readline()
                f.close()

            self.frame = MainFrame(None, channel_only, PLAYBACKMODE_INTERNAL in return_feasible_playback_modes(self.utility.getPath()), self.splash.tick)

            # Arno, 2011-06-15: VLC 1.1.10 pops up separate win, don't have two.
            self.frame.videoframe = None
            if PLAYBACKMODE_INTERNAL in return_feasible_playback_modes(self.utility.getPath()):
                vlcwrap = self.videoplayer.get_vlcwrap()

                self.frame.videoframe = VideoDummyFrame(self.frame.videoparentpanel, self.utility, vlcwrap)
                self.videoplayer.set_videoframe(self.frame.videoframe)

            if sys.platform == 'win32':
                wx.CallAfter(self.frame.top_bg.Refresh)
                wx.CallAfter(self.frame.top_bg.Layout)
            else:
                self.frame.top_bg.Layout()

            # Arno, 2007-05-03: wxWidgets 2.8.3.0 and earlier have the MIME-type for .bmp
            # files set to 'image/x-bmp' whereas 'image/bmp' is the official one.
            try:
                bmphand = None
                hands = wx.Image.GetHandlers()
                for hand in hands:
                    # print "Handler",hand.GetExtension(),hand.GetType(),hand.GetMimeType()
                    if hand.GetMimeType() == 'image/x-bmp':
                        bmphand = hand
                        break
                # wx.Image.AddHandler()
                if bmphand is not None:
                    bmphand.SetMimeType('image/bmp')
            except:
                # wx < 2.7 don't like wx.Image.GetHandlers()
                print_exc()

            self.splash.Destroy()
            self.frame.Show(True)

            self.torrentfeed = RssParser.getInstance()

            self.webUI = None
            if self.utility.config.Read('use_webui', "boolean"):
                try:
                    from Tribler.Main.webUI.webUI import WebUI
                    self.webUI = WebUI.getInstance(self.guiUtility.library_manager, self.guiUtility.torrentsearch_manager, self.utility.config.Read('webui_port', "int"))
                    self.webUI.start()
                except Exception:
                    print_exc()

            wx.CallAfter(self.PostInit2)

            # 08/02/10 Boudewijn: Working from home though console
            # doesn't allow me to press close.  The statement below
            # gracefully closes Tribler after 120 seconds.
            # wx.CallLater(120*1000, wx.GetApp().Exit)

            status = get_status_holder("LivingLab")
            status.add_reporter(NullReporter("Periodically remove all events", 0))
# status.add_reporter(LivingLabPeriodicReporter("Living lab CS reporter", 300, "Tribler client")) # Report every 5 minutes
# status.add_reporter(LivingLabPeriodicReporter("Living lab CS reporter", 30, "Tribler client")) # Report every 30 seconds - ONLY FOR TESTING

            # report client version
            status.create_and_add_event("client-startup-version", [self.utility.lang.get("version")])
            status.create_and_add_event("client-startup-build", [self.utility.lang.get("build")])
            status.create_and_add_event("client-startup-build-date", [self.utility.lang.get("build_date")])

            self.ready = True

        except Exception as e:
            self.onError(e)
            return False

    def PostInit2(self):
        self.frame.Raise()
        self.startWithRightView()
        self.set_reputation()

        s = self.utility.session
        s.add_observer(self.sesscb_ntfy_reachable, NTFY_REACHABLE, [NTFY_INSERT])
        s.add_observer(self.sesscb_ntfy_activities, NTFY_ACTIVITIES, [NTFY_INSERT], cache=10)
        s.add_observer(self.sesscb_ntfy_channelupdates, NTFY_CHANNELCAST, [NTFY_INSERT, NTFY_UPDATE, NTFY_CREATE, NTFY_STATE, NTFY_MODIFIED], cache=10)
        s.add_observer(self.sesscb_ntfy_channelupdates, NTFY_VOTECAST, [NTFY_UPDATE], cache=10)
        s.add_observer(self.sesscb_ntfy_myprefupdates, NTFY_MYPREFERENCES, [NTFY_INSERT, NTFY_UPDATE])
        s.add_observer(self.sesscb_ntfy_torrentupdates, NTFY_TORRENTS, [NTFY_UPDATE, NTFY_INSERT], cache=10)
        s.add_observer(self.sesscb_ntfy_playlistupdates, NTFY_PLAYLISTS, [NTFY_INSERT, NTFY_UPDATE])
        s.add_observer(self.sesscb_ntfy_commentupdates, NTFY_COMMENTS, [NTFY_INSERT, NTFY_DELETE])
        s.add_observer(self.sesscb_ntfy_modificationupdates, NTFY_MODIFICATIONS, [NTFY_INSERT])
        s.add_observer(self.sesscb_ntfy_moderationupdats, NTFY_MODERATIONS, [NTFY_INSERT])
        s.add_observer(self.sesscb_ntfy_markingupdates, NTFY_MARKINGS, [NTFY_INSERT])
        s.add_observer(self.sesscb_ntfy_torrentfinished, NTFY_TORRENTS, [NTFY_FINISHED])
        s.add_observer(self.sesscb_ntfy_magnet, NTFY_TORRENTS, [NTFY_MAGNET_GOT_PEERS, NTFY_MAGNET_PROGRESS, NTFY_MAGNET_STARTED, NTFY_MAGNET_CLOSE])

        self.dispersy.attach_progress_handler(self.frame.progressHandler)
        self.dispersy.callback.attach_exception_handler(self.frame.exceptionHandler)

        startWorker(None, self.loadSessionCheckpoint, delay=5.0, workerType="guiTaskQueue")

        # initialize the torrent feed thread
        channelcast = ChannelCastDBHandler.getInstance()

        def db_thread():
            return channelcast.getMyChannelId()

        def wx_thread(delayedResult):
            my_channel = delayedResult.get()
            if my_channel:
                self.torrentfeed.register(self.utility.session, my_channel)
                self.torrentfeed.addCallback(my_channel, self.guiUtility.channelsearch_manager.createTorrentFromDef)

        startWorker(wx_thread, db_thread, delay=5.0)

    def startAPI(self, progress):
        # Start Tribler Session
        defaultConfig = SessionStartupConfig()
        state_dir = defaultConfig.get_state_dir()
        if not state_dir:
            state_dir = Session.get_default_state_dir()
        cfgfilename = Session.get_default_config_filename(state_dir)

        progress('Loading sessionconfig')
        if DEBUG:
            print >> sys.stderr, "main: Session config", cfgfilename
        try:
            self.sconfig = SessionStartupConfig.load(cfgfilename)
        except:
            self.sconfig = SessionStartupConfig()
            self.sconfig.set_state_dir(state_dir)

        self.sconfig.set_install_dir(self.installdir)

        # Boudewijn, 2013-06-17: Enable Dispersy tunnel (hard-coded)
        # self.sconfig.set_dispersy_tunnel_over_swift(True)
        # Boudewijn, 2013-07-17: Disabling Dispersy tunnel (hard-coded)
        self.sconfig.set_dispersy_tunnel_over_swift(False)

        # Arno, 2010-03-31: Hard upgrade to 50000 torrents collected
        self.sconfig.set_torrent_collecting_max_torrents(50000)

        # Arno, 2012-05-21: Swift part II
        swiftbinpath = os.path.join(self.sconfig.get_install_dir(), "swift")
        if sys.platform == "darwin":
            if not os.path.exists(swiftbinpath):
                swiftbinpath = os.path.join(os.getcwdu(), "..", "MacOS", "swift")
        self.sconfig.set_swift_path(swiftbinpath)

        progress('Loading downloadconfig')
        dlcfgfilename = get_default_dscfg_filename(self.sconfig.get_state_dir())
        if DEBUG:
            print >> sys.stderr, "main: Download config", dlcfgfilename
        try:
            defaultDLConfig = DefaultDownloadStartupConfig.load(dlcfgfilename)
        except:
            defaultDLConfig = DefaultDownloadStartupConfig.getInstance()

        if not defaultDLConfig.get_dest_dir():
            defaultDLConfig.set_dest_dir(get_default_dest_dir())
        if not os.path.isdir(defaultDLConfig.get_dest_dir()):
            os.makedirs(defaultDLConfig.get_dest_dir())

        # Setting torrent collection dir based on default download dir
        if not self.sconfig.get_torrent_collecting_dir():
            self.sconfig.set_torrent_collecting_dir(os.path.join(defaultDLConfig.get_dest_dir(), STATEDIR_TORRENTCOLL_DIR))
        if not self.sconfig.get_swift_meta_dir():
            self.sconfig.set_swift_meta_dir(os.path.join(defaultDLConfig.get_dest_dir(), STATEDIR_SWIFTRESEED_DIR))

        # 15/05/12 niels: fixing swift port
        defaultDLConfig.set_swift_listen_port(7758)

        progress('Creating session/Checking database (may take a minute)')
        s = Session(self.sconfig)
        s.start()

        def define_communities():
            from Tribler.community.search.community import SearchCommunity
            from Tribler.community.allchannel.community import AllChannelCommunity
            from Tribler.community.bartercast3.community import BarterCommunity
            from Tribler.community.channel.community import ChannelCommunity
            from Tribler.community.channel.preview import PreviewChannelCommunity

            # must be called on the Dispersy thread
            dispersy.define_auto_load(SearchCommunity,
                                     (s.dispersy_member,),
                                     load=True)
            dispersy.define_auto_load(AllChannelCommunity,
                                           (s.dispersy_member,),
                                           {"auto_join_channel": True} if sys.argv[0].endswith("dispersy-channel-booster.py") else {},
                                           load=True)

            # 17/07/13 Boudewijn: the missing-member message send by the BarterCommunity on the swift port is crashing
            # 6.1 clients.  We will disable the BarterCommunity for version 6.2, giving people some time to upgrade
            # their version before enabling it again.
            # if swift_process:
            #     dispersy.define_auto_load(BarterCommunity,
            #                               (swift_process,),
            #                               load=True)

            dispersy.define_auto_load(ChannelCommunity, load=True)
            dispersy.define_auto_load(PreviewChannelCommunity)

            print >> sys.stderr, "tribler: Dispersy communities are ready"

        swift_process = s.get_swift_proc() and s.get_swift_process()
        dispersy = s.get_dispersy_instance()
        dispersy.callback.call(define_communities)
        return s

    def configure_install_dir(self, installdir):
        # Niels, 2011-03-03: Working dir sometimes set to a browsers working dir
        # only seen on windows

        # apply trick to obtain the executable location
        # see http://www.py2exe.org/index.cgi/WhereAmI
        # Niels, 2012-01-31: py2exe should only apply to windows
        if sys.platform == 'win32':
            def we_are_frozen():
                """Returns whether we are frozen via py2exe.
                This will affect how we find out where we are located."""
                return hasattr(sys, "frozen")

            def module_path():
                """ This will get us the program's directory,
                even if we are frozen using py2exe"""
                if we_are_frozen():
                    return os.path.dirname(unicode(sys.executable, sys.getfilesystemencoding()))

                filedir = os.path.dirname(unicode(__file__, sys.getfilesystemencoding()))
                return os.path.abspath(os.path.join(filedir, '..', '..'))

            return module_path()
        return installdir

    @forceWxThread
    def sesscb_ntfy_myprefupdates(self, subject, changeType, objectID, *args):
        if self.ready and self.frame.ready:
            if changeType == NTFY_INSERT:
                if self.frame.searchlist:
                    manager = self.frame.searchlist.GetManager()
                    manager.downloadStarted(objectID)

                manager = self.frame.selectedchannellist.GetManager()
                manager.downloadStarted(objectID)

            manager = self.frame.librarylist.GetManager()
            manager.downloadStarted(objectID)

    def set_reputation(self):
        def do_db():
            nr_connections = 0
            nr_channel_connections = 0
            if self.dispersy:
                for community in self.dispersy.get_communities():
                    from Tribler.community.search.community import SearchCommunity
                    from Tribler.community.allchannel.community import AllChannelCommunity

                    if isinstance(community, SearchCommunity):
                        nr_connections = community.get_nr_connections()
                    elif isinstance(community, AllChannelCommunity):
                        nr_channel_connections = community.get_nr_connections()

            return nr_connections, nr_channel_connections

        def do_wx(delayedResult):
            nr_connections, nr_channel_connections = delayedResult.get()

            # self.frame.SRstatusbar.set_reputation(myRep, total_down, total_up)

            # bitmap is 16px wide, -> but first and last pixel do not add anything.
            percentage = min(1.0, (nr_connections + 1) / 16.0)
            self.frame.SRstatusbar.SetConnections(percentage, nr_connections, nr_channel_connections)

        """ set the reputation in the GUI"""
        if self.ready and self.frame.ready:
            startWorker(do_wx, do_db, uId=u"tribler.set_reputation")
        startWorker(None, self.set_reputation, delay=5.0, workerType="guiTaskQueue")

    def _dispersy_get_barter_community(self):
        try:
            return self.dispersy.get_community(BARTER_MASTER_MEMBER_PUBLIC_KEY_DIGEST, load=False, auto_load=False)
        except KeyError:
            return None

    def sesscb_states_callback(self, dslist):
        if not self.ready:
            return (5.0, [])

        wantpeers = []
        self.ratestatecallbackcount += 1
        if DEBUG:
            torrentdb = self.utility.session.open_dbhandler(NTFY_TORRENTS)
            peerdb = self.utility.session.open_dbhandler(NTFY_PEERS)
            print >> sys.stderr, "main: Stats: Total torrents found", torrentdb.size(), "peers", peerdb.size()

        try:
            # Print stats on Console
            if DEBUG:
                if self.ratestatecallbackcount % 5 == 0:
                    for ds in dslist:
                        safename = repr(ds.get_download().get_def().get_name())
                        if DEBUG:
                            print >> sys.stderr, "%s %s %.1f%% dl %.1f ul %.1f n %d" % (safename, dlstatus_strings[ds.get_status()], 100.0 * ds.get_progress(), ds.get_current_speed(DOWNLOAD), ds.get_current_speed(UPLOAD), ds.get_num_peers())
                        # print >>sys.stderr,"main: Infohash:",`ds.get_download().get_def().get_infohash()`
                        if ds.get_status() == DLSTATUS_STOPPED_ON_ERROR:
                            print >> sys.stderr, "main: Error:", repr(ds.get_error())

            # Pass DownloadStates to libaryView
            no_collected_list = []
            try:
                coldir = os.path.basename(os.path.abspath(self.utility.session.get_torrent_collecting_dir()))
                for ds in dslist:
                    destdir = os.path.basename(ds.get_download().get_dest_dir())
                    if destdir != coldir:
                        no_collected_list.append(ds)
                # Arno, 2012-07-17: Retrieving peerlist for the DownloadStates takes CPU
                # so only do it when needed for display.
                wantpeers.extend(self.guiUtility.library_manager.download_state_callback(no_collected_list))
            except:
                print_exc()

            # Update bandwidth statistics in the Barter Community
            if not self.barter_community:
                self.barter_community = self.dispersy.callback.call(self._dispersy_get_barter_community)

            if self.barter_community and not isinstance(self.barter_community, HardKilledCommunity):
                if self.barter_community.has_been_killed:
                    # set BARTER_COMMUNITY to None.  next state callback we will again get the
                    # community resulting in the HardKilledCommunity instead
                    self.barter_community = None
                else:
                    if True in self.lastwantpeers:
                        self.dispersy.callback.register(self.barter_community.download_state_callback, (dslist, True))

                    # only request peer info every 120 intervals
                    if self.ratestatecallbackcount % 120 == 0:
                        wantpeers.append(True)

            # Find State of currently playing video
            playds = None
            d = self.videoplayer.get_vod_download()
            for ds in dslist:
                if ds.get_download() == d:
                    playds = ds

            # Apply status displaying from SwarmPlayer
            if playds:
                def do_video():
                    if playds.get_status() == DLSTATUS_HASHCHECKING:
                        progress = progress_consec = playds.get_progress()
                    else:
                        progress = playds.get_vod_prebuffering_progress()
                        progress_consec = playds.get_vod_prebuffering_progress_consec()
                    self.videoplayer.set_player_status_and_progress(progress, progress_consec, \
                                                                    playds.get_pieces_complete() if playds.get_progress() < 1.0 else [True])
                wx.CallAfter(do_video)

            # Check to see if a download has finished
            newActiveDownloads = []
            doCheckpoint = False
            for ds in dslist:
                state = ds.get_status()
                safename = ds.get_download().get_def().get_name()

                if state == DLSTATUS_DOWNLOADING:
                    newActiveDownloads.append(safename)

                elif state == DLSTATUS_SEEDING:
                    if safename in self.prevActiveDownloads:
                        download = ds.get_download()
                        cdef = download.get_def()

                        coldir = os.path.basename(os.path.abspath(self.utility.session.get_torrent_collecting_dir()))
                        destdir = os.path.basename(download.get_dest_dir())
                        if destdir != coldir:
                            hash = cdef.get_id()

                            notifier = Notifier.getInstance()
                            notifier.notify(NTFY_TORRENTS, NTFY_FINISHED, hash, safename)

                            # Arno, 2012-05-04: Swift reseeding
                            if self.utility.config.Read('swiftreseed') == 1 and cdef.get_def_type() == 'torrent' and not download.get_selected_files():
                                self.sesscb_reseed_via_swift(download)

                            doCheckpoint = True

            self.prevActiveDownloads = newActiveDownloads
            if doCheckpoint:
                self.utility.session.checkpoint()

            self.seedingmanager.apply_seeding_policy(no_collected_list)

            # Adjust speeds once every 4 seconds
            adjustspeeds = False
            if self.ratestatecallbackcount % 4 == 0:
                adjustspeeds = True

            if adjustspeeds:
                swift_dslist = [ds for ds in no_collected_list if ds.get_download().get_def().get_def_type() == 'swift']
                self.ratelimiter.add_downloadstatelist(swift_dslist)
                self.ratelimiter.adjust_speeds()

                if DEBUG_DOWNLOADS:
                    for ds in dslist:
                        cdef = ds.get_download().get_def()
                        state = ds.get_status()
                        if cdef.get_def_type() == 'swift':
                            safename = cdef.get_name()
                            print >> sys.stderr, "tribler: SW", dlstatus_strings[state], safename, ds.get_current_speed(UPLOAD)
                        else:
                            print >> sys.stderr, "tribler: BT", dlstatus_strings[state], cdef.get_name(), ds.get_current_speed(UPLOAD)

        except:
            print_exc()

        self.lastwantpeers = wantpeers
        return (1.0, wantpeers)

    def loadSessionCheckpoint(self):
        # Niels: first remove all "swift" torrent collect checkpoints
        dir = self.utility.session.get_downloads_pstate_dir()
        coldir = os.path.basename(os.path.abspath(self.utility.session.get_torrent_collecting_dir()))

        filelist = os.listdir(dir)
        filelist = [os.path.join(dir, filename) for filename in filelist if filename.endswith('.pickle')]

        for file in filelist:
            try:
                pstate = self.utility.session.lm.load_download_pstate(file)
                dlconfig = pstate['dlconfig']

                if dlconfig.get('saveas', ''):
                    destdir = os.path.basename(dlconfig['saveas'])
                    if destdir == coldir:
                        os.remove(file)
            except:
                pass

        from Tribler.Main.vwxGUI.UserDownloadChoice import UserDownloadChoice
        user_download_choice = UserDownloadChoice.get_singleton()
        initialdlstatus_dict = {}
        for id, state in user_download_choice.get_download_states().iteritems():
            if state == 'stop':
                initialdlstatus_dict[id] = DLSTATUS_STOPPED

        self.utility.session.load_checkpoint(initialdlstatus_dict=initialdlstatus_dict)

    def guiservthread_checkpoint_timer(self):
        """ Periodically checkpoint Session """
        if self.done:
            return
        try:
            print >> sys.stderr, "main: Checkpointing Session"
            self.utility.session.checkpoint()

            self.guiserver.add_task(self.guiservthread_checkpoint_timer, SESSION_CHECKPOINT_INTERVAL)
        except:
            print_exc()

    @forceWxThread
    def sesscb_ntfy_activities(self, events):
        if self.ready and self.frame.ready:
            for args in events:
                objectID = args[2]
                args = args[3:]

                self.frame.setActivity(objectID, *args)

    @forceWxThread
    def sesscb_ntfy_reachable(self, subject, changeType, objectID, msg):
        if self.ready and self.frame.ready:
            self.frame.SRstatusbar.onReachable()

    @forceWxThread
    def sesscb_ntfy_channelupdates(self, events):
        if self.ready and self.frame.ready:
            for args in events:
                subject = args[0]
                changeType = args[1]
                objectID = args[2]

                if self.frame.channellist:
                    if len(args) > 3:
                        myvote = args[3]
                    else:
                        myvote = False

                    manager = self.frame.channellist.GetManager()
                    manager.channelUpdated(objectID, subject == NTFY_VOTECAST, myvote=myvote)

                manager = self.frame.selectedchannellist.GetManager()
                manager.channelUpdated(objectID, stateChanged=changeType == NTFY_STATE, modified=changeType == NTFY_MODIFIED)

                if changeType == NTFY_CREATE:
                    if self.frame.channellist:
                        self.frame.channellist.SetMyChannelId(objectID)

                    self.torrentfeed.register(self.utility.session, objectID)
                    self.torrentfeed.addCallback(objectID, self.guiUtility.channelsearch_manager.createTorrentFromDef)

                self.frame.managechannel.channelUpdated(objectID, created=changeType == NTFY_CREATE, modified=changeType == NTFY_MODIFIED)

    @forceWxThread
    def sesscb_ntfy_torrentupdates(self, events):
        if self.ready and self.frame.ready:
            infohashes = [args[2] for args in events]

            if self.frame.searchlist:
                manager = self.frame.searchlist.GetManager()
                manager.torrentsUpdated(infohashes)

                manager = self.frame.selectedchannellist.GetManager()
                manager.torrentsUpdated(infohashes)

                manager = self.frame.playlist.GetManager()
                manager.torrentsUpdated(infohashes)

                manager = self.frame.librarylist.GetManager()
                manager.torrentsUpdated(infohashes)

    def sesscb_ntfy_torrentfinished(self, subject, changeType, objectID, *args):
        self.guiUtility.Notify("Download Completed", "Torrent '%s' has finished downloading. Now seeding." % args[0], icon='seed')

        if self.ready and self.frame.ready:
            self.guiUtility.torrentstate_manager.torrentFinished(objectID)

    def sesscb_ntfy_magnet(self, subject, changetype, objectID, *args):
        if changetype == NTFY_MAGNET_STARTED:
            self.guiUtility.library_manager.magnet_started(objectID)
        elif changetype == NTFY_MAGNET_GOT_PEERS:
            self.guiUtility.library_manager.magnet_got_peers(objectID, args[0])
        elif changetype == NTFY_MAGNET_PROGRESS:
            self.guiUtility.library_manager.magnet_got_piece(objectID, args[0])
        elif changetype == NTFY_MAGNET_CLOSE:
            self.guiUtility.library_manager.magnet_close(objectID)

    @forceWxThread
    def sesscb_ntfy_playlistupdates(self, subject, changeType, objectID, *args):
        if self.ready and self.frame.ready:
            if changeType == NTFY_INSERT:
                self.frame.managechannel.playlistCreated(objectID)

                manager = self.frame.selectedchannellist.GetManager()
                manager.playlistCreated(objectID)

            else:
                self.frame.managechannel.playlistUpdated(objectID, modified=changeType == NTFY_MODIFIED)

                if len(args) > 0:
                    infohash = args[0]
                else:
                    infohash = False
                manager = self.frame.selectedchannellist.GetManager()
                manager.playlistUpdated(objectID, infohash, modified=changeType == NTFY_MODIFIED)

                manager = self.frame.playlist.GetManager()
                manager.playlistUpdated(objectID, modified=changeType == NTFY_MODIFIED)

    @forceWxThread
    def sesscb_ntfy_commentupdates(self, subject, changeType, objectID, *args):
        if self.ready and self.frame.ready:
            self.frame.selectedchannellist.OnCommentCreated(objectID)
            self.frame.playlist.OnCommentCreated(objectID)

    @forceWxThread
    def sesscb_ntfy_modificationupdates(self, subject, changeType, objectID, *args):
        if self.ready and self.frame.ready:
            self.frame.selectedchannellist.OnModificationCreated(objectID)
            self.frame.playlist.OnModificationCreated(objectID)

    @forceWxThread
    def sesscb_ntfy_moderationupdats(self, subject, changeType, objectID, *args):
        if self.ready and self.frame.ready:
            self.frame.selectedchannellist.OnModerationCreated(objectID)
            self.frame.playlist.OnModerationCreated(objectID)

    @forceWxThread
    def sesscb_ntfy_markingupdates(self, subject, changeType, objectID, *args):
        if self.ready and self.frame.ready:
            self.frame.selectedchannellist.OnMarkingCreated(objectID)
            self.frame.playlist.OnModerationCreated(objectID)

    @forceWxThread
    def onError(self, e):
        print_exc()
        type, value, stack = sys.exc_info()
        backtrace = traceback.format_exception(type, value, stack)

        win = FeedbackWindow("Unfortunately, Tribler ran into an internal error")
        win.CreateOutputWindow('')
        for line in backtrace:
            win.write(line)

        win.ShowModal()

    def MacOpenFile(self, filename):
        print >> sys.stderr, filename
        target = FileDropTarget(self.frame)
        target.OnDropFiles(None, None, [filename])

    def OnExit(self):
        print >> sys.stderr, "main: ONEXIT"
        self.ready = False
        self.done = True

        # write all persistent data to disk
        if self.i2is:
            self.i2is.shutdown()
        if self.torrentfeed:
            self.torrentfeed.shutdown()
            self.torrentfeed.delInstance()
        if self.webUI:
            self.webUI.stop()
        if self.guiserver:
            self.guiserver.shutdown(True)
            self.guiserver.delInstance()
        if self.videoplayer:
            self.videoplayer.shutdown()
            self.videoplayer.delInstance()

        delete_status_holders()

        if self.frame:
            self.frame.Destroy()
            del self.frame

        # Don't checkpoint, interferes with current way of saving Preferences,
        # see Tribler/Main/Dialogs/abcoption.py
        if self.utility:
            # Niels: lets add a max waiting time for this session shutdown.
            session_shutdown_start = time()

            self.utility.session.shutdown(hacksessconfcheckpoint=False)

            # Arno, 2012-07-12: Shutdown should be quick
            # Niels, 2013-03-21: However, setting it too low will prevent checkpoints from being written to disk
            waittime = 60
            while not self.utility.session.has_shutdown():
                diff = time() - session_shutdown_start
                if diff > waittime:
                    print >> sys.stderr, "main: ONEXIT NOT Waiting for Session to shutdown, took too long"
                    break

                print >> sys.stderr, "main: ONEXIT Waiting for Session to shutdown, will wait for an additional %d seconds" % (waittime - diff)
                sleep(3)
            print >> sys.stderr, "main: ONEXIT Session is shutdown"

            try:
                print >> sys.stderr, "main: ONEXIT cleaning database"
                peerdb = self.utility.session.open_dbhandler(NTFY_PEERS)
                peerdb._db.clean_db(randint(0, 24) == 0, exiting=True)
            except:
                print_exc()

            print >> sys.stderr, "main: ONEXIT deleting instances"

        Session.del_instance()
        GUIUtility.delInstance()
        GUIDBProducer.delInstance()
        DefaultDownloadStartupConfig.delInstance()

        if SQLiteCacheDB.hasInstance():
            SQLiteCacheDB.getInstance().close_all()
            SQLiteCacheDB.delInstance()

        if not ALLOW_MULTIPLE:
            del self.single_instance_checker
        return 0

    def db_exception_handler(self, e):
        if DEBUG:
            print >> sys.stderr, "main: Database Exception handler called", e, "value", e.args, "#"
        try:
            if e.args[1] == "DB object has been closed":
                return  # We caused this non-fatal error, don't show.
            if self.error is not None and self.error.args[1] == e.args[1]:
                return  # don't repeat same error
        except:
            print >> sys.stderr, "main: db_exception_handler error", e, type(e)
            print_exc()
            # print_stack()

        self.onError(e)

    def getConfigPath(self):
        return self.utility.getConfigPath()

    def startWithRightView(self):
        if self.params[0] != "":
            self.guiUtility.ShowPage('my_files')

    def i2ithread_readlinecallback(self, ic, cmd):
        """ Called by Instance2Instance thread """

        print >> sys.stderr, "main: Another instance called us with cmd", cmd
        ic.close()

        if cmd.startswith('START '):
            param = cmd[len('START '):].strip()
            torrentfilename = None
            if param.startswith('http:'):
                # Retrieve from web
                f = tempfile.NamedTemporaryFile()
                n = urllib2.urlopen(param)
                data = n.read()
                f.write(data)
                f.close()
                n.close()
                torrentfilename = f.name
            else:
                torrentfilename = param

            # Switch to GUI thread
            # New for 5.0: Start in VOD mode
            def start_asked_download():
                if torrentfilename.startswith("magnet:"):
                    self.frame.startDownloadFromMagnet(torrentfilename)
                elif torrentfilename.startswith("tswift://") or torrentfilename.startswith("ppsp://"):
                    self.frame.startDownloadFromSwift(torrentfilename)
                else:
                    self.frame.startDownload(torrentfilename)
                self.guiUtility.ShowPage('my_files')

            wx.CallAfter(start_asked_download)

    def sesscb_reseed_via_swift(self, td, callback=None):
        # Arno, 2012-05-07: root hash calculation may take long time, halting
        # SessionCallbackThread meaning download statuses won't be updated.
        # Offload to diff thread.
        #
        t = Thread(target=self.workerthread_reseed_via_swift_run, args=(td, callback), name="SwiftRootHashCalculator")
        t.start()
        # apparently daemon by default

    def workerthread_reseed_via_swift_run(self, td, callback=None):
        # Open issues:
        # * how to display these "parallel" downloads in GUI?
        # * make swift reseed user configurable (see 'swiftreseed' in utility.py
        # * roothash calc on separate thread?
        # * Update pymDHT to one with swift interface.
        # * Save (infohash,roothash) pair such that when BT download is removed
        #   the other is (kept/deleted/...) too.
        #
        try:
            if prctlimported:
                prctl.set_name(currentThread().getName())

            # 1. Get torrent info
            tdef = td.get_def()
            destdir = td.get_dest_dir()

            # renaming swarmname for now not supported in swift
            if td.correctedinfoname != fix_filebasename(tdef.get_name_as_unicode()):
                return

            # 2. Convert to swift def
            sdef = SwiftDef()
            # RESEEDTODO: set to swift inf of pymDHT
            sdef.set_tracker("127.0.0.1:%d" % self.sconfig.get_swift_dht_listen_port())
            iotuples = td.get_dest_files()
            for i, o in iotuples:
                # print >>sys.stderr,"python: add_content",i,o
                if len(iotuples) == 1:
                    sdef.add_content(o)  # single file .torrent
                else:
                    xi = os.path.join(tdef.get_name_as_unicode(), i)
                    if sys.platform == "win32":
                        xi = xi.replace("\\", "/")
                    si = xi.encode("UTF-8")  # spec format
                    sdef.add_content(o, si)  # multi-file .torrent

            specpn = sdef.finalize(self.sconfig.get_swift_path(), destdir=destdir)

            # 3. Save swift files to metadata dir
            metadir = self.sconfig.get_swift_meta_dir()
            if len(iotuples) == 1:
                storagepath = iotuples[0][1]  # Point to file on disk
                metapath = os.path.join(metadir, os.path.split(storagepath)[1])

                try:
                    shutil.move(storagepath + '.mhash', metapath + '.mhash')
                    shutil.move(storagepath + '.mbinmap', metapath + '.mbinmap')
                except:
                    print_exc()

            else:
                storagepath = destdir  # Point to dest dir
                metapath = os.path.join(metadir, sdef.get_roothash_as_hex())

                # Reuse .mhash and .mbinmap (happens automatically for single-file)
                try:
                    shutil.move(specpn, metapath + '.mfspec')
                    shutil.move(specpn + '.mhash', metapath + '.mhash')
                    shutil.move(specpn + '.mbinmap', metapath + '.mbinmap')
                except:
                    print_exc()

            # 4. Start Swift download via GUI Thread
            wx.CallAfter(self.frame.startReseedSwiftDownload, tdef, storagepath, sdef)

            # 5. Call the callback to notify
            if callback:
                callback(sdef)
        except:
            print_exc()
            raise