Work on importer / SGDB integration
This commit is contained in:
@@ -60,7 +60,7 @@ class Game(Gtk.Box):
|
|||||||
blacklisted = None
|
blacklisted = None
|
||||||
game_cover = None
|
game_cover = None
|
||||||
|
|
||||||
def __init__(self, data, **kwargs):
|
def __init__(self, win, data, allow_side_effects=False, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
self.win = shared.win
|
self.win = shared.win
|
||||||
@@ -69,7 +69,8 @@ class Game(Gtk.Box):
|
|||||||
|
|
||||||
self.update_values(data)
|
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()
|
self.set_play_icon()
|
||||||
|
|
||||||
@@ -77,7 +78,6 @@ class Game(Gtk.Box):
|
|||||||
self.add_controller(self.event_contoller_motion)
|
self.add_controller(self.event_contoller_motion)
|
||||||
self.event_contoller_motion.connect("enter", self.toggle_play, False)
|
self.event_contoller_motion.connect("enter", self.toggle_play, False)
|
||||||
self.event_contoller_motion.connect("leave", self.toggle_play, None, None)
|
self.event_contoller_motion.connect("leave", self.toggle_play, None, None)
|
||||||
|
|
||||||
self.cover_button.connect("clicked", self.main_button_clicked, False)
|
self.cover_button.connect("clicked", self.main_button_clicked, False)
|
||||||
self.play_button.connect("clicked", self.main_button_clicked, True)
|
self.play_button.connect("clicked", self.main_button_clicked, True)
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import requests
|
from requests import HTTPError
|
||||||
from gi.repository import Adw, Gio, Gtk
|
from gi.repository import Adw, Gio, Gtk
|
||||||
|
|
||||||
from .create_dialog import create_dialog
|
from .create_dialog import create_dialog
|
||||||
from .save_cover import resize_cover, save_cover
|
from .steamgriddb import SGDBAuthError, SGDBError, SGDBHelper
|
||||||
from .steamgriddb import SGDBAuthError, SGDBHelper
|
|
||||||
|
|
||||||
|
|
||||||
class Importer:
|
class Importer:
|
||||||
@@ -25,11 +23,12 @@ class Importer:
|
|||||||
n_sgdb_tasks_created = 0
|
n_sgdb_tasks_created = 0
|
||||||
n_sgdb_tasks_done = 0
|
n_sgdb_tasks_done = 0
|
||||||
sgdb_cancellable = None
|
sgdb_cancellable = None
|
||||||
sgdb_error = None
|
errors = None
|
||||||
|
|
||||||
def __init__(self, win):
|
def __init__(self, win):
|
||||||
self.win = win
|
self.win = win
|
||||||
self.sources = set()
|
self.sources = set()
|
||||||
|
self.errors = []
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def n_tasks_created(self):
|
def n_tasks_created(self):
|
||||||
@@ -123,10 +122,13 @@ class Importer:
|
|||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# TODO make sources return games AND avoid duplicates
|
# Avoid duplicates
|
||||||
game_id = game.game_id
|
gid = game.game_id
|
||||||
if game.game_id in self.win.games and not self.win.games[game_id].removed:
|
if gid in self.win.games and not self.win.games[gid].removed:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Register game
|
||||||
|
self.win.games[gid] = game
|
||||||
game.save()
|
game.save()
|
||||||
self.n_games_added += 1
|
self.n_games_added += 1
|
||||||
|
|
||||||
@@ -148,49 +150,16 @@ class Importer:
|
|||||||
|
|
||||||
def sgdb_task_thread_func(self, _task, _obj, data, cancellable):
|
def sgdb_task_thread_func(self, _task, _obj, data, cancellable):
|
||||||
"""SGDB query code"""
|
"""SGDB query code"""
|
||||||
|
|
||||||
game, *_rest = data
|
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)
|
game.set_loading(1)
|
||||||
|
|
||||||
# SGDB request
|
|
||||||
sgdb = SGDBHelper(self.win)
|
sgdb = SGDBHelper(self.win)
|
||||||
try:
|
try:
|
||||||
sgdb_id = sgdb.get_game_id(game)
|
sgdb.conditionaly_update_cover(game)
|
||||||
uri = sgdb.get_game_image_uri(sgdb_id, animated=prefer_animated)
|
|
||||||
response = requests.get(uri, timeout=5)
|
|
||||||
except SGDBAuthError as error:
|
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()
|
cancellable.cancel()
|
||||||
return
|
self.errors.append(error)
|
||||||
except Exception as error: # pylint: disable=broad-exception-caught
|
except (HTTPError, SGDBError) as error:
|
||||||
logging.warning("Non auth error in SGDB query", exc_info=error)
|
self.errors.append(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))
|
|
||||||
|
|
||||||
def sgdb_task_callback(self, _obj, _result, data):
|
def sgdb_task_callback(self, _obj, _result, data):
|
||||||
"""SGDB query callback"""
|
"""SGDB query callback"""
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import logging
|
||||||
import requests
|
import requests
|
||||||
|
from requests import HTTPError
|
||||||
from gi.repository import Gio
|
from gi.repository import Gio
|
||||||
|
|
||||||
from . import shared
|
from . import shared
|
||||||
@@ -24,6 +26,10 @@ class SGDBBadRequestError(SGDBError):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class SGDBNoImageFoundError(SGDBError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SGDBHelper:
|
class SGDBHelper:
|
||||||
"""Helper class to make queries to SteamGridDB"""
|
"""Helper class to make queries to SteamGridDB"""
|
||||||
|
|
||||||
@@ -69,6 +75,66 @@ class SGDBHelper:
|
|||||||
case _:
|
case _:
|
||||||
res.raise_for_status()
|
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
|
# Current steps to save image for N games
|
||||||
# Create a task for every game
|
# Create a task for every game
|
||||||
|
|||||||
Reference in New Issue
Block a user