Esempio n. 1
0
 def add_games(self, games):
     """Add games to the store"""
     self.media_loaded = False
     if games:
         AsyncCall(self.get_missing_media, None, [game["slug"] for game in games])
     for game in list(games):
         GLib.idle_add(self.add_game, game)
Esempio n. 2
0
    def sync_library(self):
        """Synchronize games with local stuff and server."""

        def update_gui(result, error):
            self.sync_label.set_label("Synchronize library")
            self.sync_spinner.props.active = False
            self.sync_button.set_sensitive(True)
            if error:
                if isinstance(error, http.UnauthorizedAccess):
                    GLib.idle_add(self.show_invalid_credential_warning)
                else:
                    GLib.idle_add(self.show_library_sync_error)
                return
            if result:
                added_ids, updated_ids = result
                self.game_store.add_games_by_ids(added_ids)
                for game_id in updated_ids.difference(added_ids):
                    self.game_store.update_game_by_id(game_id)
            else:
                logger.error("No results returned when syncing the library")

        self.sync_label.set_label("Synchronizing…")
        self.sync_spinner.props.active = True
        self.sync_button.set_sensitive(False)
        AsyncCall(sync_from_remote, update_gui)
Esempio n. 3
0
    def sync_library(self):
        """Synchronize games with local stuff and server."""
        def update_gui(result, error):
            if error:
                logger.error("Failed to synchrone library: %s", error)
                return
            if result:
                added_ids, updated_ids = result

                # sqlite limits the number of query parameters to 999, to
                # bypass that limitation, divide the query in chunks
                size = 999
                added_games = chain.from_iterable([
                    pga.get_games_where(
                        id__in=list(added_ids)[page * size:page * size + size])
                    for page in range(math.ceil(len(added_ids) / size))
                ])
                self.game_list += added_games
                self.switch_splash_screen()
                self.view.populate_games(added_games)
                GLib.idle_add(self.update_existing_games, added_ids,
                              updated_ids, True)
            else:
                logger.error("No results returned when syncing the library")

        self.set_status("Syncing library")
        AsyncCall(sync_from_remote, update_gui)
Esempio n. 4
0
    def _iter_commands(self, result=None, exception=None):
        if result == 'STOP' or self.cancelled:
            return

        self.parent.set_status("Installing game data")
        self.parent.add_spinner()
        self.parent.continue_button.hide()

        commands = self.script.get('installer', [])
        if exception:
            self.parent.on_install_error(repr(exception))
        elif self.current_command < len(commands):
            try:
                command = commands[self.current_command]
            except KeyError:
                raise ScriptingError(
                    'Installer commands are not formatted correctly')
            self.current_command += 1
            method, params = self._map_command(command)
            if isinstance(params, dict):
                status_text = params.pop("description", None)
            else:
                status_text = None
            if status_text:
                self.parent.set_status(status_text)
            logger.debug('Installer command: %s', command)
            AsyncCall(method, self._iter_commands, params)
        else:
            self._finish_install()
Esempio n. 5
0
 def on_refresh_clicked(self, button):
     """Reload the service games"""
     button.set_sensitive(False)
     if self.service.online and not self.service.is_connected():
         self.service.logout()
         return
     AsyncCall(self.service.reload, self.service_load_cb)
Esempio n. 6
0
    def install_steam_game(self, runner_class=None, is_game_files=False):
        """Launch installation of a steam game.

        runner_class: class of the steam runner to use
        is_game_files: whether game data is added to game_files
        """

        # Check if Steam is installed, save the method's arguments so it can
        # be called again once Steam is installed.
        self.steam_data['callback_args'] = (runner_class, is_game_files)

        steam_runner = self._get_steam_runner(runner_class)
        self.steam_data['is_game_files'] = is_game_files
        appid = self.steam_data['appid']
        if not steam_runner.get_game_path_from_appid(appid):
            logger.debug("Installing steam game %s", appid)
            steam_runner.config = LutrisConfig(runner_slug=self.runner)
            if 'arch' in self.steam_data:
                steam_runner.config.game_config['arch'] = self.steam_data[
                    'arch']
            AsyncCall(steam_runner.install_game, None, appid, is_game_files)

            self.install_start_time = time.localtime()
            self.steam_poll = GLib.timeout_add(
                2000, self._monitor_steam_game_install)
            self.abort_current_task = (
                lambda: steam_runner.remove_game_data(appid=appid))
            return 'STOP'
        elif is_game_files:
            self._append_steam_data_to_files(runner_class)
        else:
            self.target_path = self._get_steam_game_path()
Esempio n. 7
0
    def get_games_from_filters(self):
        if self.filters.get("service"):
            service_name = self.filters["service"]
            if service_name in services.get_services():
                self.service = services.get_services()[service_name]()
                if self.service.online:
                    self.service.connect("service-login",
                                         self.on_service_games_updated)
                    self.service.connect("service-logout",
                                         self.on_service_logout)
                self.service.connect("service-games-loaded",
                                     self.on_service_games_updated)

                service_games = ServiceGameCollection.get_for_service(
                    service_name)
                if service_games:
                    return [
                        game for game in sorted(
                            service_games,
                            key=lambda game: game.get(self.view_sorting) or
                            game["name"],
                            reverse=not self.view_sorting_ascending,
                        ) if self.game_matches(game)
                    ]

                if not self.service.online or self.service.is_connected():
                    AsyncCall(self.service.load, None)
                    spinner = Gtk.Spinner(visible=True)
                    spinner.start()
                    self.blank_overlay.add(spinner)
                else:
                    self.blank_overlay.add(
                        Gtk.Label(
                            _("Connect your %s account to access your games") %
                            self.service.name,
                            visible=True,
                        ))
                self.blank_overlay.props.visible = True
                return
            self.unset_service()
        dynamic_categories = {
            "running": self.get_running_games,
            "installed": self.get_installed_games,
        }
        if self.filters.get("dynamic_category") in dynamic_categories:
            return dynamic_categories[self.filters["dynamic_category"]]()
        self.unset_service()
        if self.filters.get("category"):
            game_ids = categories_db.get_game_ids_for_category(
                self.filters["category"])
            return games_db.get_games_by_ids(game_ids)

        searches, filters, excludes = self.get_sql_filters()
        return games_db.get_games(
            searches=searches,
            filters=filters,
            excludes=excludes,
            sorts=self.sort_params,
        )
Esempio n. 8
0
 def on_connect_clicked(self, _button):
     if self.service.is_authenticated():
         AsyncCall(self._connect_button_toggle, None)
         self.service.logout()
     else:
         self._connect_button_toggle()
         self.service.login()
     return False
Esempio n. 9
0
 def load_games(self, force_reload=False):
     """Load the list of games in a treeview"""
     if self.games_loaded and not force_reload:
         return
     if self.service.ONLINE and not self.service.is_connected():
         return
     syncer = self.service.SYNCER()
     AsyncCall(syncer.load, self.on_games_loaded, force_reload)
Esempio n. 10
0
    def update_existing_games(self, added, updated, first_run=False):
        for game_id in updated.difference(added):
            self.view.update_row(pga.get_game_by_field(game_id, 'id'))

        if first_run:
            icons_sync = AsyncCall(self.sync_icons, callback=None)
            self.threads_stoppers.append(icons_sync.stop_request.set)
            self.set_status("")
Esempio n. 11
0
    def on_sync_button_clicked(self, _button, sync_with_lutris_method):  # pylint: disable=unused-argument
        """Called when the sync button is clicked.

        Launches the import of selected games
        """
        syncer = self.service.SYNCER()
        AsyncCall(syncer.sync, self.on_service_synced,
                  self.get_imported_games())
Esempio n. 12
0
 def on_connect_clicked(self, _button):
     if self.service.is_authenticated():
         logger.debug("Disconnecting from %s", self.identifier)
         AsyncCall(self._connect_button_toggle, None)
         self.service.logout()
     else:
         logger.debug("Connecting to %s", self.identifier)
         self._connect_button_toggle()
         self.service.login()
     return False
Esempio n. 13
0
    def _on_toggle_with_callback(self, widget, _gparam, option):
        """Action for the checkbox's toggled signal. With callback method"""

        option_name = option["option"]
        callback = option["callback"]
        callback_on = option.get("callback_on")
        if widget.get_active() == callback_on or callback_on is None:
            AsyncCall(callback, self._on_callback_finished, widget, option, self.config)
        else:
            self.option_changed(widget, option_name, widget.get_active())
Esempio n. 14
0
    def __init__(self, game_id, parent=None):
        super().__init__(parent=parent)
        self.set_size_request(640, 128)
        self.game = Game(game_id)
        self.delete_files = False
        container = Gtk.VBox(visible=True)
        self.get_content_area().add(container)

        title_label = Gtk.Label(visible=True)
        title_label.set_line_wrap(True)
        title_label.set_alignment(0, 0.5)
        title_label.set_line_wrap_mode(Pango.WrapMode.WORD_CHAR)
        title_label.set_markup(
            "<span font_desc='14'><b>Uninstall %s</b></span>" %
            gtk_safe(self.game.name))

        container.pack_start(title_label, False, False, 4)

        self.folder_label = Gtk.Label(visible=True)
        self.folder_label.set_alignment(0, 0.5)

        self.delete_button = Gtk.Button(_("Uninstall"), visible=True)
        self.delete_button.connect("clicked", self.on_delete_clicked)

        if not self.game.directory:
            self.folder_label.set_markup("No file will be deleted")
        elif len(get_games(searches={"directory": self.game.directory})) > 1:
            self.folder_label.set_markup(
                "The folder %s is used by other games and will be kept." %
                self.game.directory)
        elif is_removeable(self.game.directory):
            self.delete_button.set_sensitive(False)
            self.folder_label.set_markup("<i>Calculating size…</i>")
            AsyncCall(get_disk_size, self.folder_size_cb, self.game.directory)
        else:
            self.folder_label.set_markup(
                "Content of %s are protected and will not be deleted." %
                reverse_expanduser(self.game.directory))
        container.pack_start(self.folder_label, False, False, 4)

        self.confirm_delete_button = Gtk.CheckButton()
        self.confirm_delete_button.set_active(True)
        container.pack_start(self.confirm_delete_button, False, False, 4)

        button_box = Gtk.HBox(visible=True)
        button_box.set_margin_top(30)
        style_context = button_box.get_style_context()
        style_context.add_class("linked")
        cancel_button = Gtk.Button(_("Cancel"), visible=True)
        cancel_button.connect("clicked", self.on_close)
        button_box.add(cancel_button)
        button_box.add(self.delete_button)
        container.pack_end(button_box, False, False, 0)
        self.show()
Esempio n. 15
0
    def update_existing_games(self, added, updated, first_run=False):
        """???"""
        for game_id in updated.difference(added):
            game = pga.get_game_by_field(game_id, "id")
            self.view.update_row(game["id"], game["year"], game["playtime"])

        if first_run:
            self.update_games(added)
            game_slugs = [game["slug"] for game in self.game_list]
            AsyncCall(resources.get_missing_media, self.on_media_returned,
                      game_slugs)
Esempio n. 16
0
 def scan_folder(self):
     """Scan a folder of already installed games"""
     self.title_label.set_markup("<b>Import games from a folder</b>")
     self.listbox.destroy()
     script_dlg = DirectoryDialog(_("Select folder to scan"))
     if not script_dlg.folder:
         self.destroy()
         return
     spinner = Gtk.Spinner(visible=True)
     spinner.start()
     self.vbox.pack_start(spinner, False, False, 18)
     AsyncCall(scan_directory, self._on_folder_scanned, script_dlg.folder)
Esempio n. 17
0
 def service_load_cb(self, games, error):
     if games is None:
         logger.warning("No game returned from the service")
     if not error and not games and self.service.id == "steam":
         # This should not be handled here, the steam service should raise an error
         error = _(
             "Failed to load games. Check that your profile is set to public during the sync."
         )
     if error:
         ErrorDialog(str(error))
     AsyncCall(self.service.add_installed_games, None)
     GLib.timeout_add(5000, self.enable_refresh_button)
Esempio n. 18
0
 def __init__(
     self,
     icon_type,
     filter_installed,
     sort_key,
     sort_ascending,
     show_installed_first=False,
 ):
     super(GameStore, self).__init__()
     self.games = pga.get_games(show_installed_first=show_installed_first)
     self.games_to_refresh = set()
     self.icon_type = icon_type
     self.filter_installed = filter_installed
     self.show_installed_first = show_installed_first
     self.filter_text = None
     self.filter_runner = None
     self.filter_platform = None
     self.store = Gtk.ListStore(
         int,
         str,
         str,
         Pixbuf,
         str,
         str,
         str,
         str,
         int,
         str,
         bool,
         int,
         str,
         str,
         str,
     )
     if show_installed_first:
         self.store.set_sort_column_id(COL_INSTALLED,
                                       Gtk.SortType.DESCENDING)
     else:
         self.store.set_sort_column_id(COL_NAME, Gtk.SortType.ASCENDING)
     self.prevent_sort_update = False  # prevent recursion with signals
     self.modelfilter = self.store.filter_new()
     self.modelfilter.set_visible_func(self.filter_view)
     self.modelsort = Gtk.TreeModelSort.sort_new_with_model(
         self.modelfilter)
     self.modelsort.connect("sort-column-changed",
                            self.on_sort_column_changed)
     self.sort_view(sort_key, sort_ascending)
     self.medias = {"banner": {}, "icon": {}}
     self.media_loaded = False
     self.connect('media-loaded', self.on_media_loaded)
     self.connect('icon-loaded', self.on_icon_loaded)
     AsyncCall(self.get_missing_media)
Esempio n. 19
0
    def load_games(self, force_reload=False):
        """Load the list of games in a treeview"""
        if self.games_loaded and not force_reload:
            return
        if self.service.ONLINE and not self.service.is_connected():
            return
        syncer = self.service.SYNCER()
        if force_reload:
            self.service.SERVICE.wipe_game_cache()

        self.is_connecting = True
        self.swap_content(self.get_content_widget())
        AsyncCall(syncer.load, self.on_games_loaded)
Esempio n. 20
0
 def on_zoom_changed(self, adjustment):
     """Handler for zoom modification"""
     media_index = round(adjustment.props.value)
     adjustment.props.value = media_index
     service = self.service if self.service else LutrisService
     media_services = list(service.medias.keys())
     if len(media_services) <= media_index:
         media_index = media_services.index(service.default_format)
     icon_type = media_services[media_index]
     if icon_type != self.icon_type:
         self.save_icon_type(icon_type)
         self.reload_service_media()
         self.show_spinner()
         AsyncCall(self.game_store.load_icons, self.icons_loaded_cb)
Esempio n. 21
0
    def update_existing_games(self, added, updated, first_run=False):
        """Updates the games in the view from the callback of the method
        Still, working on this docstring.
        If the implementation is shit,  the docstring is as well.
        """
        for game_id in updated.difference(added):
            game = pga.get_game_by_field(game_id, "id")
            self.view.update_row(game["id"], game["year"], game["playtime"])

        if first_run:
            self.update_games(added)
            game_slugs = [game["slug"] for game in self.game_list]
            AsyncCall(resources.get_missing_media, self.on_media_returned,
                      game_slugs)
Esempio n. 22
0
 def __init__(self, init_lutris):
     super().__init__()
     self.set_size_request(320, 60)
     self.set_border_width(24)
     vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 12)
     label = Gtk.Label("Lutris is starting...")
     vbox.add(label)
     self.progress = Gtk.ProgressBar(visible=True)
     self.progress.set_pulse_step(0.1)
     vbox.add(self.progress)
     self.get_content_area().add(vbox)
     GLib.timeout_add(125, self.show_progress)
     self.show_all()
     AsyncCall(self.initialize, self.init_cb, init_lutris)
Esempio n. 23
0
 def install_steam_game(self):
     """Launch installation of a steam game"""
     if self.runner.get_game_path_from_appid(appid=self.appid):
         logger.info("Steam game %s is already installed", self.appid)
         self.emit("game-installed", self.appid)
     else:
         logger.debug("Installing steam game %s", self.appid)
         self.runner.config = LutrisConfig(runner_slug=self.runner.name)
         # FIXME Find a way to bring back arch support
         # steam_runner.config.game_config["arch"] = self.steam_data["arch"]
         AsyncCall(self.runner.install_game, self.on_steam_game_installed, self.appid)
         self.install_start_time = time.localtime()
         self.steam_poll = GLib.timeout_add(2000, self._monitor_steam_game_install)
         self.stop_func = lambda: self.runner.remove_game_data(appid=self.appid)
Esempio n. 24
0
    def update_search_results(self):
        # Don't start a search while another is going; defer it instead.
        if self.search_spinner.get_visible():
            self.search_timer_id = GLib.timeout_add(750,
                                                    self.update_search_results)
            return

        self.search_timer_id = None

        if self.text_query:
            self.search_spinner.show()
            self.search_spinner.start()
            AsyncCall(api.search_games, self.update_search_results_cb,
                      self.text_query)
Esempio n. 25
0
 def __init__(self, init_lutris):
     super().__init__()
     self.set_size_request(320, 60)
     self.set_border_width(24)
     self.set_decorated(False)
     vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 12)
     label = Gtk.Label(_("Checking for runtime updates, please wait…"))
     vbox.add(label)
     self.progress = Gtk.ProgressBar(visible=True)
     self.progress.set_pulse_step(0.1)
     vbox.add(self.progress)
     self.get_content_area().add(vbox)
     GLib.timeout_add(125, self.show_progress)
     self.show_all()
     AsyncCall(self.initialize, self.init_cb, init_lutris)
Esempio n. 26
0
 def get_api_games(self):
     """Return games from the lutris API"""
     if not self.filters.get("text"):
         return []
     api_games = api.search_games(self.filters["text"])
     if "icon" in self.icon_type:
         api_field = "icon_url"
         _service_media = LutrisIcon
     else:
         api_field = "banner_url"
         _service_media = LutrisBanner
     AsyncCall(download_icons, self.icons_download_cb,
               {g["slug"]: g[api_field]
                for g in api_games}, _service_media())
     return api_games
Esempio n. 27
0
    def update_existing_games(self, added, updated, first_run=False):
        """???"""
        for game_id in updated.difference(added):
            # XXX this might not work if the game has no 'item' set
            logger.debug("Updating row for ID %s", game_id)
            self.view.update_row(pga.get_game_by_field(game_id, 'id'))

        if first_run:
            logger.info("Setting up view for first run")
            for game_id in added:
                logger.debug("Adding %s", game_id)
                self.add_game_to_view(game_id)
            icons_sync = AsyncCall(self.sync_icons, callback=None)
            self.threads_stoppers.append(icons_sync.stop_request.set)
            self.set_status("")
Esempio n. 28
0
    def sync_library(self):
        """Synchronize games with local stuff and server."""
        def update_gui(result, error):
            if result:
                added_ids, updated_ids = result
                added_games = pga.get_game_by_field(added_ids, 'id', all=True)
                self.game_list += added_games
                self.view.populate_games(added_games)
                self.switch_splash_screen()
                GLib.idle_add(self.update_existing_games, added_ids,
                              updated_ids, True)
            else:
                logger.error("No results returned when syncing the library")

        self.set_status("Syncing library")
        AsyncCall(sync_from_remote, update_gui)
Esempio n. 29
0
    def sync_services(self):
        """Sync local lutris library with current Steam games and desktop games"""
        def full_sync(syncer_cls):
            syncer = syncer_cls()
            games = syncer.load()
            return syncer.sync(games, full=True)

        def on_sync_complete(response, errors):
            """Callback to update the view on sync complete"""
            if errors:
                logger.error("Sync failed: %s", errors)
            added_games, removed_games = response
            self.update_games(added_games)
            for game_id in removed_games:
                self.remove_game_from_view(game_id)

        for service in get_services_synced_at_startup():
            AsyncCall(full_sync, on_sync_complete, service.SYNCER)
Esempio n. 30
0
 def on_delete_clicked(self, button):
     button.set_sensitive(False)
     if self.delete_files and not hasattr(self.game.runner,
                                          "no_game_remove_warning"):
         dlg = QuestionDialog({
             "question":
             _("Please confirm.\nEverything under <b>%s</b>\n"
               "will be deleted.") % gtk_safe(self.game.directory),
             "title":
             _("Permanently delete files?"),
         })
         if dlg.result != Gtk.ResponseType.YES:
             button.set_sensitive(True)
             return
     if self.delete_files:
         self.folder_label.set_markup(
             "Uninstalling game and deleting files...")
     else:
         self.folder_label.set_markup("Uninstalling game...")
     AsyncCall(self.game.remove, self.delete_cb, self.delete_files)