Merge branch 'main' into libadwaita-1.4

This commit is contained in:
kramo
2023-08-30 10:21:28 +02:00
parent e67977287d
commit 89bc0877fd
73 changed files with 4569 additions and 3197 deletions

View File

@@ -17,13 +17,12 @@
#
# SPDX-License-Identifier: GPL-3.0-or-later
from typing import Callable, Any
from typing import Any, Callable
from gi.repository import Gio
from src.game import Game
from src.store.managers.manager import Manager
from src.utils.task import Task
class AsyncManager(Manager):
@@ -49,11 +48,10 @@ class AsyncManager(Manager):
self, game: Game, additional_data: dict, callback: Callable[["Manager"], Any]
) -> None:
"""Create a task to process the game in a separate thread"""
task = Task.new(None, self.cancellable, self._task_callback, (callback,))
task.set_task_data((game, additional_data))
task.run_in_thread(self._task_thread_func)
task = Gio.Task.new(None, self.cancellable, self._task_callback, (callback,))
task.run_in_thread(lambda *_: self._task_thread_func((game, additional_data)))
def _task_thread_func(self, _task, _source_object, data, _cancellable):
def _task_thread_func(self, data):
"""Task thread entry point"""
game, additional_data, *_rest = data
self.run(game, additional_data)

View File

@@ -22,14 +22,14 @@ from pathlib import Path
from typing import NamedTuple
import requests
from gi.repository import Gio, GdkPixbuf
from gi.repository import GdkPixbuf, Gio
from requests.exceptions import HTTPError, SSLError
from src import shared
from src.game import Game
from src.store.managers.manager import Manager
from src.store.managers.steam_api_manager import SteamAPIManager
from src.utils.save_cover import resize_cover, save_cover
from src.utils.save_cover import convert_cover, save_cover
class ImageSize(NamedTuple):
@@ -110,18 +110,16 @@ class CoverManager(Manager):
stretch = 1 - (resized_height / cover_size.height)
return stretch <= max_stretch
def save_composited_cover(
def composite_cover(
self,
game: Game,
image_path: Path,
scale: float = 1,
blur_size: ImageSize = ImageSize(2, 2),
) -> None:
) -> GdkPixbuf.Pixbuf:
"""
Save the image composited with a background blur.
Return the image composited with a background blur.
If the image is stretchable, just stretch it.
:param game: The game to save the cover for
:param path: Path where the source image is located
:param scale:
Scale of the smalled image side
@@ -130,14 +128,15 @@ class CoverManager(Manager):
"""
# Load source image
source = GdkPixbuf.Pixbuf.new_from_file(str(image_path))
source = GdkPixbuf.Pixbuf.new_from_file(
str(convert_cover(image_path, resize=False))
)
source_size = ImageSize(source.get_width(), source.get_height())
cover_size = ImageSize._make(shared.image_size)
# Stretch if possible
if scale == 1 and self.is_stretchable(source_size, cover_size):
save_cover(game.game_id, resize_cover(pixbuf=source))
return
return source
# Create the blurred cover background
# fmt: off
@@ -164,7 +163,7 @@ class CoverManager(Manager):
GdkPixbuf.InterpType.BILINEAR,
255,
)
save_cover(game.game_id, resize_cover(pixbuf=cover))
return cover
def main(self, game: Game, additional_data: dict) -> None:
if game.blacklisted:
@@ -185,13 +184,15 @@ class CoverManager(Manager):
continue
# Icon cover
if key == "local_icon_path":
self.save_composited_cover(
game,
image_path,
scale=0.7,
blur_size=ImageSize(1, 2),
)
return
composite_kwargs = {}
self.save_composited_cover(game, image_path)
if key == "local_icon_path":
composite_kwargs["scale"] = 0.7
composite_kwargs["blur_size"] = ImageSize(1, 2)
save_cover(
game.game_id,
convert_cover(
pixbuf=self.composite_cover(image_path, **composite_kwargs)
),
)

View File

@@ -46,7 +46,7 @@ class Manager(ErrorProducer):
max_tries: int = 3
@property
def name(self):
def name(self) -> str:
return type(self).__name__
@abstractmethod
@@ -59,13 +59,13 @@ class Manager(ErrorProducer):
* May raise other exceptions that will be reported
"""
def run(self, game: Game, additional_data: dict):
def run(self, game: Game, additional_data: dict) -> None:
"""Handle errors (retry, ignore or raise) that occur in the manager logic"""
# Keep track of the number of tries
tries = 1
def handle_error(error: Exception):
def handle_error(error: Exception) -> None:
nonlocal tries
# If FriendlyError, handle its cause instead
@@ -83,11 +83,11 @@ class Manager(ErrorProducer):
retrying_format = "Retrying %s in %s for %s"
unretryable_format = "Unretryable %s in %s for %s"
if error in self.continue_on:
if type(error) in self.continue_on:
# Handle skippable errors (skip silently)
return
if error in self.retryable_on:
if type(error) in self.retryable_on:
if tries > self.max_tries:
# Handle being out of retries
logging.error(out_of_retries_format, *log_args)
@@ -104,7 +104,7 @@ class Manager(ErrorProducer):
logging.error(unretryable_format, *log_args, exc_info=error)
self.report_error(base_error)
def try_manager_logic():
def try_manager_logic() -> None:
try:
self.main(game, additional_data)
except Exception as error: # pylint: disable=broad-exception-caught

View File

@@ -24,13 +24,13 @@ from requests.exceptions import HTTPError, SSLError
from src.errors.friendly_error import FriendlyError
from src.game import Game
from src.store.managers.async_manager import AsyncManager
from src.store.managers.steam_api_manager import SteamAPIManager
from src.store.managers.cover_manager import CoverManager
from src.store.managers.steam_api_manager import SteamAPIManager
from src.utils.steamgriddb import SGDBAuthError, SGDBHelper
class SGDBManager(AsyncManager):
"""Manager in charge of downloading a game's cover from steamgriddb"""
"""Manager in charge of downloading a game's cover from SteamGridDB"""
run_after = (SteamAPIManager, CoverManager)
retryable_on = (HTTPError, SSLError, ConnectionError, JSONDecodeError)

View File

@@ -23,8 +23,8 @@ from urllib3.exceptions import ConnectionError as Urllib3ConnectionError
from src.game import Game
from src.store.managers.async_manager import AsyncManager
from src.utils.steam import (
SteamGameNotFoundError,
SteamAPIHelper,
SteamGameNotFoundError,
SteamNotAGameError,
SteamRateLimiter,
)
@@ -44,7 +44,7 @@ class SteamAPIManager(AsyncManager):
self.steam_api_helper = SteamAPIHelper(self.steam_rate_limiter)
def main(self, game: Game, additional_data: dict) -> None:
# Skip non-steam games
# Skip non-Steam games
appid = additional_data.get("steam_appid", None)
if appid is None:
return

View File

@@ -83,7 +83,7 @@ class Pipeline(GObject.Object):
progress = 1
return progress
def advance(self):
def advance(self) -> None:
"""Spawn tasks for managers that are able to run for a game"""
# Separate blocking / async managers

View File

@@ -18,7 +18,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later
import logging
from typing import Any, Generator, MutableMapping
from typing import Any, Generator, MutableMapping, Optional
from src import shared
from src.game import Game
@@ -77,13 +77,15 @@ class Store:
except KeyError:
return default
def add_manager(self, manager: Manager, in_pipeline=True):
def add_manager(self, manager: Manager, in_pipeline: bool = True) -> None:
"""Add a manager to the store"""
manager_type = type(manager)
self.managers[manager_type] = manager
self.toggle_manager_in_pipelines(manager_type, in_pipeline)
def toggle_manager_in_pipelines(self, manager_type: type[Manager], enable: bool):
def toggle_manager_in_pipelines(
self, manager_type: type[Manager], enable: bool
) -> None:
"""Change if a manager should run in new pipelines"""
if enable:
self.pipeline_managers.add(self.managers[manager_type])
@@ -108,8 +110,8 @@ class Store:
pass
def add_game(
self, game: Game, additional_data: dict, run_pipeline=True
) -> Pipeline | None:
self, game: Game, additional_data: dict, run_pipeline: bool = True
) -> Optional[Pipeline]:
"""Add a game to the app"""
# Ignore games from a newer spec version