From 2b92417674eb05e19b3becaad582cbbd9571b02b Mon Sep 17 00:00:00 2001 From: GeoffreyCoulaud Date: Thu, 11 May 2023 00:53:18 +0200 Subject: [PATCH] Work on after import dialogs --- src/importer/importer.py | 109 +++++++++++++++++++++++++++++---------- src/utils/steamgriddb.py | 70 ++++++++++++------------- 2 files changed, 116 insertions(+), 63 deletions(-) diff --git a/src/importer/importer.py b/src/importer/importer.py index 42c4946..de72958 100644 --- a/src/importer/importer.py +++ b/src/importer/importer.py @@ -3,11 +3,11 @@ from pathlib import Path from threading import Lock, Thread import requests -from requests import HTTPError from gi.repository import Adw, Gio, Gtk +from .create_dialog import create_dialog from .save_cover import resize_cover, save_cover -from .steamgriddb import SGDBHelper, SGDBError +from .steamgriddb import SGDBAuthError, SGDBHelper class Importer: @@ -23,6 +23,8 @@ class Importer: sources = None # Internal values + sgdb_error = None + sgdb_error_lock = None source_threads = None sgdb_threads = None progress_lock = None @@ -40,6 +42,7 @@ class Importer: self.games_lock = Lock() self.progress_lock = Lock() self.sgdb_threads_lock = Lock() + self.sgdb_error_lock = Lock() self.win = win @property @@ -74,8 +77,51 @@ class Importer: ) self.import_dialog.present() - def close_dialog(self): - self.import_dialog.close() + def create_sgdb_error_dialog(self): + create_dialog( + self.win, + _("Couldn't Connect to SteamGridDB"), + str(self.sgdb_error), + "open_preferences", + _("Preferences"), + ).connect("response", self.on_dialog_response, "sgdb") + + def create_import_done_dialog(self): + games_no = len(self.games) + if games_no == 0: + create_dialog( + self.win, + _("No Games Found"), + _("No new games were found on your system."), + "open_preferences", + _("Preferences"), + ).connect("response", self.on_dialog_response) + elif games_no == 1: + create_dialog( + self.win, + _("Game Imported"), + _("Successfully imported 1 game."), + ).connect("response", self.on_dialog_response) + elif games_no > 1: + create_dialog( + self.win, + _("Games Imported"), + # The variable is the number of games + _("Successfully imported {} games.").format(games_no), + ).connect("response", self.on_dialog_response) + + def on_dialog_response(self, _widget, response, *args): + if response == "open_preferences": + page, expander_row, *_rest = args + self.win.get_application().on_preferences_action( + page_name=page, expander_row=expander_row + ) + # HACK SGDB manager should be in charge of its error dialog + elif self.sgdb_error is not None: + self.create_sgdb_error_dialog() + self.sgdb_error = None + # TODO additional steam libraries tip + # (should be handled by the source somehow) def update_progressbar(self): self.progressbar.set_fraction(self.progress) @@ -131,6 +177,7 @@ class ImporterThread(Thread): thread.join() self.importer.import_dialog.close() + self.importer.create_import_done_dialog() class SourceThread(Thread): @@ -177,12 +224,10 @@ class SourceThread(Thread): # Start sgdb lookup for game in another thread # HACK move to a game manager - use_sgdb = self.win.schema.get_boolean("sgdb") - if use_sgdb and not game.blacklisted: - sgdb_thread = SGDBThread(game, self.win, self.importer) - with self.importer.sgdb_threads_lock: - self.importer.sgdb_threads.append(sgdb_thread) - sgdb_thread.start() + sgdb_thread = SGDBThread(game, self.win, self.importer) + with self.importer.sgdb_threads_lock: + self.importer.sgdb_threads.append(sgdb_thread) + sgdb_thread.start() class SGDBThread(Thread): @@ -198,8 +243,14 @@ class SGDBThread(Thread): self.win = win self.importer = importer - def run(self): - """Thread entry point""" + def conditionnaly_fetch_cover(self): + use_sgdb = self.win.schema.get_boolean("sgdb") + if ( + not use_sgdb + or self.game.blacklisted + or self.importer.sgdb_error is not None + ): + return # Check if we should query SGDB prefer_sgdb = self.win.schema.get_boolean("sgdb-prefer") @@ -216,26 +267,32 @@ class SGDBThread(Thread): self.game.set_loading(1) - # Add image from sgdb + # SGDB request sgdb = SGDBHelper(self.win) try: sgdb_id = sgdb.get_game_id(self.game) uri = sgdb.get_game_image_uri(sgdb_id, animated=prefer_animated) response = requests.get(uri, timeout=5) - except HTTPError as _error: - # TODO handle http errors - pass - except SGDBError as _error: - # TODO handle SGDB API errors - pass - else: - tmp_file = Gio.File.new_tmp()[0] - tmp_file_path = tmp_file.get_path() - Path(tmp_file_path).write_bytes(response.content) - save_cover( - self.win, self.game.game_id, resize_cover(self.win, tmp_file_path) - ) + except SGDBAuthError as error: + with self.importer.sgdb_error_lock: + if self.importer.sgdb_error is None: + self.importer.sgdb_error = error + logging.error("SGDB Auth error occured") + return + except Exception as error: # pylint: disable=broad-exception-caught + logging.warning("Non auth error in SGDB query", exc_info=error) + return + + # Image saving + tmp_file = Gio.File.new_tmp()[0] + tmp_file_path = tmp_file.get_path() + Path(tmp_file_path).write_bytes(response.content) + save_cover(self.win, self.game.game_id, resize_cover(self.win, tmp_file_path)) self.game.set_loading(0) + + def run(self): + """Thread entry point""" + self.conditionnaly_fetch_cover() with self.importer.progress_lock: self.importer.counts[self.game.source.id]["covers"] += 1 diff --git a/src/utils/steamgriddb.py b/src/utils/steamgriddb.py index 9e4453f..6eb27cc 100644 --- a/src/utils/steamgriddb.py +++ b/src/utils/steamgriddb.py @@ -12,6 +12,18 @@ class SGDBError(Exception): pass +class SGDBAuthError(SGDBError): + pass + + +class SGDBGameNotFoundError(SGDBError): + pass + + +class SGDBBadRequestError(SGDBError): + pass + + class SGDBHelper: """Helper class to make queries to SteamGridDB""" @@ -27,51 +39,35 @@ class SGDBHelper: headers = {"Authorization": f"Bearer {key}"} return headers - # TODO delegate that to the app - def create_exception_dialog(self, exception): - dialog = create_dialog( - self.win, - _("Couldn't Connect to SteamGridDB"), - exception, - "open_preferences", - _("Preferences"), - ) - dialog.connect("response", self.on_exception_dialog_response) - - # TODO same as create_exception_dialog - def on_exception_dialog_response(self, _widget, response): - if response == "open_preferences": - self.win.get_application().on_preferences_action(page_name="sgdb") - def get_game_id(self, game): """Get grid results for a game. Can raise an exception.""" - - # Request - res = requests.get( - f"{self.base_url}search/autocomplete/{game.name}", - headers=self.auth_headers, - timeout=5, - ) - if res.status_code == 200: - return res.json()["data"][0]["id"] - - # HTTP error - res.raise_for_status() - - # SGDB API error - res_json = res.json() - if "error" in tuple(res_json): - raise SGDBError(res_json["errors"]) - raise SGDBError(res.status_code) + uri = f"{self.base_url}search/autocomplete/{game.name}" + res = requests.get(uri, headers=self.auth_headers, timeout=5) + match res.status_code: + case 200: + return res.json()["data"][0]["id"] + case 401: + raise SGDBAuthError(res.json()["errors"][0]) + case 404: + raise SGDBGameNotFoundError(res.status_code) + case _: + res.raise_for_status() def get_image_uri(self, game_id, animated=False): """Get the image for a SGDB game id""" uri = f"{self.base_url}grids/game/{game_id}?dimensions=600x900" if animated: uri += "&types=animated" - grid = requests.get(uri, headers=self.auth_headers, timeout=5) - image_uri = grid.json()["data"][0]["url"] - return image_uri + res = requests.get(uri, headers=self.auth_headers, timeout=5) + match res.status_code: + case 200: + return res.json()["data"][0]["url"] + case 401: + raise SGDBAuthError(res.json()["errors"][0]) + case 404: + raise SGDBGameNotFoundError(res.status_code) + case _: + res.raise_for_status() # Current steps to save image for N games