Various changes

- Removed useless format manager
- Moved pipeline to its own file
- Fixed steam source next not returning game
- Changed pipeline order
This commit is contained in:
GeoffreyCoulaud
2023-05-24 19:34:07 +02:00
parent e7e30c8ac5
commit 1d2253ff94
8 changed files with 103 additions and 106 deletions

View File

@@ -4,6 +4,7 @@ from gi.repository import Adw, Gio, Gtk
from src import shared from src import shared
from src.utils.task import Task from src.utils.task import Task
from src.store.pipeline import Pipeline
# pylint: disable=too-many-instance-attributes # pylint: disable=too-many-instance-attributes
@@ -83,6 +84,7 @@ class Importer:
if not source.is_installed: if not source.is_installed:
logging.info("Source %s skipped, not installed", source.id) logging.info("Source %s skipped, not installed", source.id)
return return
logging.info("Scanning source %s", source.id)
# Initialize source iteration # Initialize source iteration
iterator = iter(source) iterator = iter(source)
@@ -104,8 +106,10 @@ class Importer:
continue continue
# Register game # Register game
pipeline: Pipeline = shared.store.add_game(game)
if pipeline is not None:
logging.info("Imported %s (%s)", game.name, game.game_id) logging.info("Imported %s (%s)", game.name, game.game_id)
shared.store.add_game(game) pipeline.connect("manager-done", self.manager_done_callback)
self.n_games_added += 1 self.n_games_added += 1
def source_task_callback(self, _obj, _result, data): def source_task_callback(self, _obj, _result, data):
@@ -113,10 +117,18 @@ class Importer:
source, *_rest = data source, *_rest = data
logging.debug("Import done for source %s", source.id) logging.debug("Import done for source %s", source.id)
self.n_source_tasks_done += 1 self.n_source_tasks_done += 1
# TODO remove, should be handled by manager_done_callback
self.update_progressbar() self.update_progressbar()
if self.finished: if self.finished:
self.import_callback() self.import_callback()
def manager_done_callback(self, pipeline: Pipeline):
"""Callback called when a pipeline for a game has advanced"""
# TODO (optional) update progress bar more precisely from here
# TODO get number of games really added here (eg. exlude blacklisted)
# TODO trigger import_callback only when all pipelines have finished
pass
def import_callback(self): def import_callback(self):
"""Callback called when importing has finished""" """Callback called when importing has finished"""
logging.info("Import done") logging.info("Import done")

View File

@@ -91,6 +91,8 @@ class SteamSourceIterator(SourceIterator):
if cover_path.is_file(): if cover_path.is_file():
save_cover(game.game_id, resize_cover(cover_path)) save_cover(game.game_id, resize_cover(cover_path))
return game
class SteamSource(Source): class SteamSource(Source):
name = "Steam" name = "Steam"

View File

@@ -43,7 +43,6 @@ from src.importer.sources.steam_source import (
from src.preferences import PreferencesWindow from src.preferences import PreferencesWindow
from src.store.managers.display_manager import DisplayManager from src.store.managers.display_manager import DisplayManager
from src.store.managers.file_manager import FileManager from src.store.managers.file_manager import FileManager
from src.store.managers.format_update_manager import FormatUpdateManager
from src.store.managers.sgdb_manager import SGDBManager from src.store.managers.sgdb_manager import SGDBManager
from src.store.managers.steam_api_manager import SteamAPIManager from src.store.managers.steam_api_manager import SteamAPIManager
from src.store.store import Store from src.store.store import Store
@@ -80,7 +79,6 @@ class CartridgesApplication(Adw.Application):
# Create the games store ready to load games from disk # Create the games store ready to load games from disk
if not shared.store: if not shared.store:
shared.store = Store() shared.store = Store()
shared.store.add_manager(FormatUpdateManager())
shared.store.add_manager(DisplayManager()) shared.store.add_manager(DisplayManager())
# Load games from disk # Load games from disk
@@ -238,6 +236,6 @@ class CartridgesApplication(Adw.Application):
def main(version): # pylint: disable=unused-argument def main(version): # pylint: disable=unused-argument
log_level = os.environ.get("LOGLEVEL", "ERROR").upper() log_level = os.environ.get("LOGLEVEL", "ERROR").upper()
logging.basicConfig(level="DEBUG") # TODO remove debug logging.basicConfig(level="INFO") # TODO remove before release, use env
app = CartridgesApplication() app = CartridgesApplication()
return app.run(sys.argv) return app.run(sys.argv)

View File

@@ -1,14 +1,12 @@
from src.game import Game from src.game import Game
from src.store.managers.format_update_manager import FormatUpdateManager
from src.store.managers.manager import Manager from src.store.managers.manager import Manager
from src.store.managers.sgdb_manager import SGDBManager
from src.store.managers.steam_api_manager import SteamAPIManager from src.store.managers.steam_api_manager import SteamAPIManager
class FileManager(Manager): class FileManager(Manager):
"""Manager in charge of saving a game to a file""" """Manager in charge of saving a game to a file"""
run_after = set((SteamAPIManager, SGDBManager, FormatUpdateManager)) run_after = set((SteamAPIManager,))
def run(self, game: Game) -> None: def run(self, game: Game) -> None:
game.save() game.save()

View File

@@ -1,19 +0,0 @@
from src.store.managers.manager import Manager
from src.game import Game
class FormatUpdateManager(Manager):
"""Class in charge of migrating a game from an older format"""
def v1_5_to_v2_0(self, game: Game) -> None:
"""Convert a game from v1.5 format to v2.0 format"""
if game.blacklisted is None:
game.blacklisted = False
if game.removed is None:
game.removed = False
game.version = 2.0
def run(self, game: Game) -> None:
if game.version is None:
self.v1_5_to_v2_0(game)
game.save()

View File

@@ -3,11 +3,14 @@ from requests import HTTPError
from src.game import Game from src.game import Game
from src.store.managers.manager import Manager from src.store.managers.manager import Manager
from src.utils.steamgriddb import SGDBAuthError, SGDBError, SGDBHelper from src.utils.steamgriddb import SGDBAuthError, SGDBError, SGDBHelper
from src.store.managers.steam_api_manager import SteamAPIManager
class SGDBManager(Manager): class SGDBManager(Manager):
"""Manager in charge of downloading a game's cover from steamgriddb""" """Manager in charge of downloading a game's cover from steamgriddb"""
run_after = set((SteamAPIManager,))
def run(self, game: Game) -> None: def run(self, game: Game) -> None:
try: try:
sgdb = SGDBHelper() sgdb = SGDBHelper()

80
src/store/pipeline.py Normal file
View File

@@ -0,0 +1,80 @@
from typing import Iterable
from gi.repository import GObject
from src.game import Game
from src.store.managers.manager import Manager
from src.utils.task import Task
class Pipeline(GObject.Object):
"""Class representing a set of managers for a game"""
game: Game
waiting: set[Manager]
running: set[Manager]
done: set[Manager]
def __init__(self, game: Game, managers: Iterable[Manager]) -> None:
super().__init__()
self.game = game
self.waiting = set(managers)
self.running = set()
self.done = set()
@property
def not_done(self) -> set[Manager]:
"""Get the managers that are not done yet"""
return self.waiting | self.running
@property
def blocked(self) -> set[Manager]:
"""Get the managers that cannot run because their dependencies aren't done"""
blocked = set()
for manager_a in self.waiting:
for manager_b in self.not_done:
if manager_a == manager_b:
continue
if type(manager_b) in manager_a.run_after:
blocked.add(manager_a)
return blocked
@property
def ready(self) -> set[Manager]:
"""Get the managers that can be run"""
return self.waiting - self.blocked
def advance(self):
"""Spawn tasks for managers that are able to run for a game"""
for manager in self.ready:
self.waiting.remove(manager)
self.running.add(manager)
data = (manager,)
task = Task.new(self, manager.cancellable, self.manager_task_callback, data)
task.set_task_data(data)
task.run_in_thread(self.manager_task_thread_func)
@GObject.Signal(name="manager-started", arg_types=(object,))
def manager_started(self, manager: Manager) -> None:
"""Signal emitted when a manager is started"""
pass
def manager_task_thread_func(self, _task, _source_object, data, cancellable):
"""Thread function for manager tasks"""
manager, *_rest = data
self.emit("manager-started", manager)
manager.run(self.game)
@GObject.Signal(name="manager-done", arg_types=(object,))
def manager_done(self, manager: Manager) -> None:
"""Signal emitted when a manager is done"""
pass
def manager_task_callback(self, _source_object, _result, data):
"""Callback function for manager tasks"""
manager, *_rest = data
self.running.remove(manager)
self.done.add(manager)
self.emit("manager-done", manager)
self.advance()

View File

@@ -1,84 +1,7 @@
from typing import Iterable
from gi.repository import GObject
from src import shared from src import shared
from src.game import Game from src.game import Game
from src.store.managers.manager import Manager from src.store.managers.manager import Manager
from src.utils.task import Task from src.store.pipeline import Pipeline
class Pipeline(GObject.Object):
"""Class representing a set of managers for a game"""
game: Game
waiting: set[Manager]
running: set[Manager]
done: set[Manager]
def __init__(self, game: Game, managers: Iterable[Manager]) -> None:
super().__init__()
self.game = game
self.waiting = set(managers)
self.running = set()
self.done = set()
@property
def not_done(self) -> set[Manager]:
"""Get the managers that are not done yet"""
return self.waiting | self.running
@property
def blocked(self) -> set[Manager]:
"""Get the managers that cannot run because their dependencies aren't done"""
blocked = set()
for manager_a in self.waiting:
for manager_b in self.not_done:
if manager_a == manager_b:
continue
if type(manager_b) in manager_a.run_after:
blocked.add(manager_a)
return blocked
@property
def ready(self) -> set[Manager]:
"""Get the managers that can be run"""
return self.waiting - self.blocked
def advance(self):
"""Spawn tasks for managers that are able to run for a game"""
for manager in self.ready:
self.waiting.remove(manager)
self.running.add(manager)
data = (manager,)
task = Task.new(self, manager.cancellable, self.manager_task_callback, data)
task.set_task_data(data)
task.run_in_thread(self.manager_task_thread_func)
@GObject.Signal(name="manager-started", arg_types=(object,))
def manager_started(self, manager: Manager) -> None:
"""Signal emitted when a manager is started"""
pass
def manager_task_thread_func(self, _task, _source_object, data, cancellable):
"""Thread function for manager tasks"""
manager, *_rest = data
self.emit("manager-started", manager)
manager.run(self.game)
@GObject.Signal(name="manager-done", arg_types=(object,))
def manager_done(self, manager: Manager) -> None:
"""Signal emitted when a manager is done"""
pass
def manager_task_callback(self, _source_object, _result, data):
"""Callback function for manager tasks"""
manager, *_rest = data
self.running.remove(manager)
self.done.add(manager)
self.emit("manager-done", manager)
self.advance()
class Store: class Store: