Work on after import dialogs

This commit is contained in:
GeoffreyCoulaud
2023-05-11 00:53:18 +02:00
parent 211c5d670b
commit 2b92417674
2 changed files with 116 additions and 63 deletions

View File

@@ -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

View File

@@ -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