From 0c6c0ea4674840ed7f5cb63d12388b0931f0d546 Mon Sep 17 00:00:00 2001 From: GeoffreyCoulaud Date: Thu, 18 May 2023 16:18:31 +0200 Subject: [PATCH] Work on importer / SGDB integration --- src/game.py | 6 ++-- src/importer/importer.py | 59 +++++++++-------------------------- src/utils/steamgriddb.py | 66 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 48 deletions(-) diff --git a/src/game.py b/src/game.py index e46f81a..c568c2e 100644 --- a/src/game.py +++ b/src/game.py @@ -60,7 +60,7 @@ class Game(Gtk.Box): blacklisted = None game_cover = None - def __init__(self, data, **kwargs): + def __init__(self, win, data, allow_side_effects=False, **kwargs): super().__init__(**kwargs) self.win = shared.win @@ -69,7 +69,8 @@ class Game(Gtk.Box): self.update_values(data) - self.win.games[self.game_id] = self + if allow_side_effects: + self.win.games[self.game_id] = self self.set_play_icon() @@ -77,7 +78,6 @@ class Game(Gtk.Box): self.add_controller(self.event_contoller_motion) self.event_contoller_motion.connect("enter", self.toggle_play, False) self.event_contoller_motion.connect("leave", self.toggle_play, None, None) - self.cover_button.connect("clicked", self.main_button_clicked, False) self.play_button.connect("clicked", self.main_button_clicked, True) diff --git a/src/importer/importer.py b/src/importer/importer.py index 2371c7b..7868cdc 100644 --- a/src/importer/importer.py +++ b/src/importer/importer.py @@ -1,12 +1,10 @@ import logging -from pathlib import Path -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 SGDBAuthError, SGDBHelper +from .steamgriddb import SGDBAuthError, SGDBError, SGDBHelper class Importer: @@ -25,11 +23,12 @@ class Importer: n_sgdb_tasks_created = 0 n_sgdb_tasks_done = 0 sgdb_cancellable = None - sgdb_error = None + errors = None def __init__(self, win): self.win = win self.sources = set() + self.errors = [] @property def n_tasks_created(self): @@ -123,10 +122,13 @@ class Importer: ) continue - # TODO make sources return games AND avoid duplicates - game_id = game.game_id - if game.game_id in self.win.games and not self.win.games[game_id].removed: + # Avoid duplicates + gid = game.game_id + if gid in self.win.games and not self.win.games[gid].removed: continue + + # Register game + self.win.games[gid] = game game.save() self.n_games_added += 1 @@ -148,49 +150,16 @@ class Importer: def sgdb_task_thread_func(self, _task, _obj, data, cancellable): """SGDB query code""" - game, *_rest = data - - use_sgdb = self.win.schema.get_boolean("sgdb") - if not use_sgdb or game.blacklisted: - return - - # Check if we should query SGDB - prefer_sgdb = self.win.schema.get_boolean("sgdb-prefer") - prefer_animated = self.win.schema.get_boolean("sgdb-animated") - image_trunk = self.win.covers_dir / game.game_id - still = image_trunk.with_suffix(".tiff") - animated = image_trunk.with_suffix(".gif") - - # Breaking down the condition - is_missing = not still.is_file() and not animated.is_file() - is_not_best = not animated.is_file() and prefer_animated - if not (is_missing or is_not_best or prefer_sgdb): - return - game.set_loading(1) - - # SGDB request sgdb = SGDBHelper(self.win) try: - sgdb_id = sgdb.get_game_id(game) - uri = sgdb.get_game_image_uri(sgdb_id, animated=prefer_animated) - response = requests.get(uri, timeout=5) + sgdb.conditionaly_update_cover(game) except SGDBAuthError as error: - # On auth error, cancel all present and future SGDB tasks for this import - self.sgdb_error = error - logging.error("SGDB Auth error occured", exc_info=error) cancellable.cancel() - 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, game.game_id, resize_cover(self.win, tmp_file_path)) + self.errors.append(error) + except (HTTPError, SGDBError) as error: + self.errors.append(error) def sgdb_task_callback(self, _obj, _result, data): """SGDB query callback""" diff --git a/src/utils/steamgriddb.py b/src/utils/steamgriddb.py index 6eb27cc..2a71f9b 100644 --- a/src/utils/steamgriddb.py +++ b/src/utils/steamgriddb.py @@ -1,6 +1,8 @@ from pathlib import Path +import logging import requests +from requests import HTTPError from gi.repository import Gio from . import shared @@ -24,6 +26,10 @@ class SGDBBadRequestError(SGDBError): pass +class SGDBNoImageFoundError(SGDBError): + pass + + class SGDBHelper: """Helper class to make queries to SteamGridDB""" @@ -69,6 +75,66 @@ class SGDBHelper: case _: res.raise_for_status() + def conditionaly_update_cover(self, game): + """Update the game's cover if appropriate""" + + # Obvious skips + use_sgdb = self.win.schema.get_boolean("sgdb") + if not use_sgdb or game.blacklisted: + return + + image_trunk = self.win.covers_dir / game.game_id + still = image_trunk.with_suffix(".tiff") + uri_kwargs = image_trunk.with_suffix(".gif") + prefer_sgdb = self.win.schema.get_boolean("sgdb-prefer") + + # Do nothing if file present and not prefer SGDB + if not prefer_sgdb and (still.is_file() or uri_kwargs.is_file()): + return + + # Get ID for the game + try: + sgdb_id = self.get_game_id(game) + except (HTTPError, SGDBError) as error: + logging.warning( + "Error while getting SGDB ID for %s", game.name, exc_info=error + ) + raise error + + # Build different SGDB options to try + image_uri_kwargs_sets = [{"animated": False}] + if self.win.schema.get_boolean("sgdb-animated"): + image_uri_kwargs_sets.insert(0, {"animated": True}) + + # Download covers + for uri_kwargs in image_uri_kwargs_sets: + try: + uri = self.get_game_image_uri(sgdb_id, **uri_kwargs) + response = requests.get(uri, timeout=5) + 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, game.game_id, resize_cover(self.win, tmp_file_path) + ) + except SGDBAuthError as error: + # Let caller handle auth errors + raise error + except (HTTPError, SGDBError) as error: + logging.warning("Error while getting image", exc_info=error) + continue + else: + # Stop as soon as one is finished + return + + # No image was added + logging.warning( + 'No matching image found for game "%s" (SGDB ID %d)', + game.name, + sgdb_id, + ) + raise SGDBNoImageFoundError() + # Current steps to save image for N games # Create a task for every game