diff --git a/data/gtk/help-overlay.blp b/data/gtk/help-overlay.blp
index 0fa1d35..9982000 100644
--- a/data/gtk/help-overlay.blp
+++ b/data/gtk/help-overlay.blp
@@ -8,49 +8,54 @@ ShortcutsWindow help_overlay {
max-height: 10;
ShortcutsGroup {
- title: C_("shortcut window", "General");
+ title: C_("shortcuts window", "General");
ShortcutsShortcut {
- title: C_("shortcut window", "Quit");
+ title: C_("shortcuts window", "Quit");
action-name: "app.quit";
}
ShortcutsShortcut {
- title: C_("shortcut window", "Search");
+ title: C_("shortcuts window", "Search");
action-name: "win.toggle_search";
}
ShortcutsShortcut {
- title: C_("shortcut window", "Show preferences");
+ title: C_("shortcuts window", "Show preferences");
action-name: "app.preferences";
}
ShortcutsShortcut {
- title: C_("shortcut window", "Shortcuts");
+ title: C_("shortcuts window", "Shortcuts");
action-name: "win.show-help-overlay";
}
ShortcutsShortcut {
- title: C_("shortcut window", "Undo");
+ title: C_("shortcuts window", "Undo");
action-name: "win.undo_remove";
}
ShortcutsShortcut {
- title: C_("shortcut window", "Open menu");
+ title: C_("shortcuts window", "Open menu");
action-name: "win.open_menu";
}
}
ShortcutsGroup {
- title: C_("shortcut window", "Games");
+ title: C_("shortcuts window", "Games");
ShortcutsShortcut {
- title: C_("shortcut window", "Add new game");
+ title: C_("shortcuts window", "Add new game");
action-name: "app.add_game";
}
ShortcutsShortcut {
- title: C_("shortcut window", "Show hidden games");
+ title: C_("shortcuts window", "Import games");
+ action-name: "app.import";
+ }
+
+ ShortcutsShortcut {
+ title: C_("shortcuts window", "Show hidden games");
action-name: "win.show_hidden";
}
}
diff --git a/data/gtk/preferences.blp b/data/gtk/preferences.blp
index f0bb691..f1ea811 100644
--- a/data/gtk/preferences.blp
+++ b/data/gtk/preferences.blp
@@ -3,7 +3,7 @@ using Adw 1;
template PreferencesWindow : Adw.PreferencesWindow {
search-enabled: false;
- default-height: 550;
+ default-height: 500;
Adw.PreferencesPage page {
Adw.PreferencesGroup general_group {
@@ -39,6 +39,14 @@ template PreferencesWindow : Adw.PreferencesWindow {
Adw.PreferencesGroup steam_group {
title: _("Steam");
+ Adw.ActionRow {
+ title: _("Import From Steam");
+
+ Switch steam_switch {
+ valign: center;
+ }
+ }
+
Adw.ActionRow {
title: _("Steam Install Location");
subtitle: _("Directory to use when importing games");
@@ -77,6 +85,14 @@ template PreferencesWindow : Adw.PreferencesWindow {
Adw.PreferencesGroup heroic_group {
title: _("Heroic");
+ Adw.ActionRow {
+ title: _("Import From Heroic");
+
+ Switch heroic_switch {
+ valign: center;
+ }
+ }
+
Adw.ActionRow {
title: _("Heroic Install Location");
subtitle: _("Directory to use when importing games");
@@ -115,6 +131,14 @@ template PreferencesWindow : Adw.PreferencesWindow {
Adw.PreferencesGroup bottles_group {
title: _("Bottles");
+ Adw.ActionRow {
+ title: _("Import From Bottles");
+
+ Switch bottles_switch {
+ valign: center;
+ }
+ }
+
Adw.ActionRow {
title: _("Bottles Install Location");
subtitle: _("Directory to use when importing games");
diff --git a/data/gtk/window.blp b/data/gtk/window.blp
index 9a4767a..29b3493 100644
--- a/data/gtk/window.blp
+++ b/data/gtk/window.blp
@@ -384,23 +384,9 @@ menu add_games {
}
}
section {
- submenu {
- label: _("Import from");
- item {
- label: _("Steam");
- action: "app.steam_import";
- }
-
- item {
- label: _("Heroic");
- action: "app.heroic_import";
- }
-
- item {
- label: _("Bottles");
- action: "app.bottles_import";
- hidden-when: "action-disabled";
- }
+ item {
+ label: _("Import");
+ action: "app.import";
}
}
}
diff --git a/data/hu.kramo.Cartridges.gschema.xml b/data/hu.kramo.Cartridges.gschema.xml
index dc2708e..b0304d0 100644
--- a/data/hu.kramo.Cartridges.gschema.xml
+++ b/data/hu.kramo.Cartridges.gschema.xml
@@ -10,12 +10,18 @@
false
+
+ true
+
"~/.steam/"
[]
+
+ true
+
"~/.var/app/com.heroicgameslauncher.hgl/config/heroic/"
@@ -28,6 +34,9 @@
true
+
+ true
+
"~/.var/app/com.usebottles.bottles/data/bottles/"
diff --git a/src/game.py b/src/game.py
index d0e8fff..484c492 100644
--- a/src/game.py
+++ b/src/game.py
@@ -25,7 +25,7 @@ import sys
from gi.repository import GdkPixbuf, Gio, Gtk
-from .save_games import save_games
+from .save_game import save_game
@Gtk.Template(resource_path="/hu/kramo/Cartridges/gtk/game.ui")
@@ -120,7 +120,7 @@ class game(Gtk.Box): # pylint: disable=invalid-name
data["hidden"] = not data["hidden"]
- save_games({self.game_id: data})
+ save_game(data)
def get_cover(self):
diff --git a/src/main.py b/src/main.py
index b346887..24a3281 100644
--- a/src/main.py
+++ b/src/main.py
@@ -33,8 +33,9 @@ from .bottles_parser import bottles_parser
from .create_details_window import create_details_window
from .get_games import get_games
from .heroic_parser import heroic_parser
+from .importer import Importer
from .preferences import PreferencesWindow
-from .save_games import save_games
+from .save_game import save_game
from .steam_parser import steam_parser
from .window import CartridgesWindow
@@ -49,18 +50,13 @@ class CartridgesApplication(Adw.Application):
self.create_action(
"preferences", self.on_preferences_action, ["comma"]
)
- self.create_action("steam_import", self.on_steam_import_action)
- self.create_action("heroic_import", self.on_heroic_import_action)
- self.create_action("bottles_import", self.on_bottles_import_action)
self.create_action("launch_game", self.on_launch_game_action)
self.create_action("hide_game", self.on_hide_game_action)
self.create_action("edit_details", self.on_edit_details_action)
self.create_action("add_game", self.on_add_game_action, ["n"])
+ self.create_action("import", self.on_import_action, ["i"])
self.create_action("remove_game", self.on_remove_game_action)
- if os.name == "nt":
- self.lookup_action("bottles_import").set_enabled(False)
-
self.win = None
def do_activate(self): # pylint: disable=arguments-differ
@@ -134,20 +130,6 @@ class CartridgesApplication(Adw.Application):
def on_preferences_action(self, _widget, _callback=None):
PreferencesWindow(self.win).present()
- def on_steam_import_action(self, _widget, _callback=None):
- # Handle the updating of games inside of the module because the function is async
- steam_parser(self.win, self.on_steam_import_action)
-
- def on_heroic_import_action(self, _widget, _callback=None):
- games = heroic_parser(self.win, self.on_heroic_import_action)
- save_games(games)
- self.win.update_games(games.keys())
-
- def on_bottles_import_action(self, _widget, _callback=None):
- games = bottles_parser(self.win, self.on_bottles_import_action)
- save_games(games)
- self.win.update_games(games.keys())
-
def on_launch_game_action(self, _widget, _callback=None):
# Launch the game and update the last played value
@@ -155,7 +137,7 @@ class CartridgesApplication(Adw.Application):
data = get_games([game_id])[game_id]
data["last_played"] = int(time.time())
- save_games({game_id: data})
+ save_game(data)
self.win.games[game_id].launch()
@@ -176,13 +158,33 @@ class CartridgesApplication(Adw.Application):
def on_add_game_action(self, _widget, _callback=None):
create_details_window(self.win)
+ def on_import_action(self, _widget, _callback=None):
+ self.win.importer = Importer(self.win)
+
+ self.win.importer.blocker = True
+
+ if self.win.schema.get_boolean("steam"):
+ steam_parser(self.win)
+
+ if self.win.schema.get_boolean("heroic"):
+ heroic_parser(self.win)
+
+ if self.win.schema.get_boolean("bottles"):
+ bottles_parser(self.win)
+
+ self.win.importer.blocker = False
+
+ if self.win.importer.import_dialog.is_visible and self.win.importer.queue == 0:
+ self.win.importer.queue = 1
+ self.win.importer.save_game()
+
def on_remove_game_action(self, _widget, _callback=None):
# Add "removed=True" to the game properties so it can be deleted on next init
game_id = self.win.active_game_id
data = get_games([game_id])[game_id]
data["removed"] = True
- save_games({game_id: data})
+ save_game(data)
self.win.update_games([game_id])
if self.win.stack.get_visible_child() == self.win.overview:
diff --git a/src/meson.build b/src/meson.build
index 32b0b14..f2cd13c 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -22,11 +22,12 @@ cartridges_sources = [
'window.py',
'preferences.py',
'game.py',
+ 'utils/importer.py',
'utils/steam_parser.py',
'utils/heroic_parser.py',
'utils/bottles_parser.py',
'utils/get_games.py',
- 'utils/save_games.py',
+ 'utils/save_game.py',
'utils/save_cover.py',
'utils/create_dialog.py',
'utils/create_details_window.py'
diff --git a/src/preferences.py b/src/preferences.py
index b4a655d..400a3d2 100644
--- a/src/preferences.py
+++ b/src/preferences.py
@@ -32,17 +32,20 @@ class PreferencesWindow(Adw.PreferencesWindow):
cover_launches_game_switch = Gtk.Template.Child()
high_quality_images_switch = Gtk.Template.Child()
+ steam_switch = Gtk.Template.Child()
steam_file_chooser_button = Gtk.Template.Child()
steam_extra_file_chooser_button = Gtk.Template.Child()
steam_clear_button_revealer = Gtk.Template.Child()
steam_clear_button = Gtk.Template.Child()
+ heroic_switch = Gtk.Template.Child()
heroic_file_chooser_button = Gtk.Template.Child()
heroic_epic_switch = Gtk.Template.Child()
heroic_gog_switch = Gtk.Template.Child()
heroic_sideloaded_switch = Gtk.Template.Child()
bottles_group = Gtk.Template.Child()
+ bottles_switch = Gtk.Template.Child()
bottles_file_chooser_button = Gtk.Template.Child()
def __init__(self, parent_widget, **kwargs):
@@ -68,6 +71,18 @@ class PreferencesWindow(Adw.PreferencesWindow):
"active",
Gio.SettingsBindFlags.DEFAULT,
)
+ schema.bind(
+ "steam",
+ self.steam_switch,
+ "active",
+ Gio.SettingsBindFlags.DEFAULT,
+ )
+ schema.bind(
+ "heroic",
+ self.heroic_switch,
+ "active",
+ Gio.SettingsBindFlags.DEFAULT,
+ )
schema.bind(
"heroic-import-epic",
self.heroic_epic_switch,
@@ -86,6 +101,12 @@ class PreferencesWindow(Adw.PreferencesWindow):
"active",
Gio.SettingsBindFlags.DEFAULT,
)
+ schema.bind(
+ "bottles",
+ self.bottles_switch,
+ "active",
+ Gio.SettingsBindFlags.DEFAULT,
+ )
filechooser = Gtk.FileDialog()
diff --git a/src/utils/bottles_parser.py b/src/utils/bottles_parser.py
index 315ca5b..9beee8d 100644
--- a/src/utils/bottles_parser.py
+++ b/src/utils/bottles_parser.py
@@ -21,24 +21,19 @@ import os
import time
import yaml
-from gi.repository import GLib, Gtk
-
-from .create_dialog import create_dialog
-from .save_cover import save_cover
-def bottles_parser(parent_widget, action):
+def bottles_parser(parent_widget):
schema = parent_widget.schema
bottles_dir = os.path.expanduser(schema.get_string("bottles-location"))
- def bottles_not_found():
+ if not os.path.isfile(os.path.join(bottles_dir, "library.yml")):
if os.path.exists(
os.path.expanduser("~/.var/app/com.usebottles.bottles/data/bottles/")
):
schema.set_string(
"bottles-location", "~/.var/app/com.usebottles.bottles/data/bottles/"
)
- action(None, None)
elif os.path.exists(
os.path.join(
os.getenv("XDG_DATA_HOME")
@@ -54,42 +49,10 @@ def bottles_parser(parent_widget, action):
"bottles",
),
)
- action(None, None)
else:
- filechooser = Gtk.FileDialog.new()
-
- def set_bottles_dir(_source, result, _unused):
- try:
- schema.set_string(
- "bottles-location",
- filechooser.select_folder_finish(result).get_path(),
- )
- action(None, None)
- except GLib.GError:
- return
-
- def choose_folder(_widget):
- filechooser.select_folder(parent_widget, None, set_bottles_dir, None)
-
- def response(widget, response):
- if response == "choose_folder":
- choose_folder(widget)
-
- create_dialog(
- parent_widget,
- _("Couldn't Import Games"),
- _("The Bottles directory cannot be found."),
- "choose_folder",
- _("Set Bottles Location"),
- ).connect("response", response)
-
- if not os.path.isfile(os.path.join(bottles_dir, "library.yml")):
- bottles_not_found()
- return {}
+ return
bottles_dir = os.path.expanduser(schema.get_string("bottles-location"))
-
- bottles_games = {}
current_time = int(time.time())
with open(os.path.join(bottles_dir, "library.yml"), "r") as open_file:
@@ -97,6 +60,10 @@ def bottles_parser(parent_widget, action):
library = yaml.load(data, Loader=yaml.Loader)
+ importer = parent_widget.importer
+ importer.total_queue += len(library)
+ importer.queue += len(library)
+
for game in library:
game = library[game]
values = {}
@@ -107,6 +74,7 @@ def bottles_parser(parent_widget, action):
values["game_id"] in parent_widget.games
and not parent_widget.games[values["game_id"]].removed
):
+ importer.save_game()
continue
values["name"] = game["name"]
@@ -120,9 +88,8 @@ def bottles_parser(parent_widget, action):
values["last_played"] = 0
if game["thumbnail"]:
- save_cover(
- values,
- parent_widget,
+ importer.save_cover(
+ values["game_id"],
os.path.join(
bottles_dir,
"bottles",
@@ -131,27 +98,4 @@ def bottles_parser(parent_widget, action):
game["thumbnail"].split(":")[1],
),
)
-
- bottles_games[values["game_id"]] = values
-
- if not bottles_games:
- create_dialog(
- parent_widget,
- _("No Games Found"),
- _("No new games were found in the Bottles library."),
- )
- elif len(bottles_games) == 1:
- create_dialog(
- parent_widget,
- _("Bottles Games Imported"),
- _("Successfully imported 1 game."),
- )
- elif len(bottles_games) > 1:
- games_no = str(len(bottles_games))
- create_dialog(
- parent_widget,
- _("Bottles Games Imported"),
- # The variable is the number of games
- _(f"Successfully imported {games_no} games."),
- )
- return bottles_games
+ importer.save_game(values)
diff --git a/src/utils/create_details_window.py b/src/utils/create_details_window.py
index 4163c9a..e37e6f5 100644
--- a/src/utils/create_details_window.py
+++ b/src/utils/create_details_window.py
@@ -26,7 +26,7 @@ from gi.repository import Adw, GdkPixbuf, Gio, GLib, GObject, Gtk
from .create_dialog import create_dialog
from .save_cover import save_cover
-from .save_games import save_games
+from .save_game import save_game
def create_details_window(parent_widget, game_id=None):
@@ -268,7 +268,7 @@ def create_details_window(parent_widget, game_id=None):
return
if pixbuf is not None:
- save_cover(None, parent_widget, None, pixbuf, game_id)
+ save_cover(parent_widget, game_id, None, pixbuf)
values["name"] = final_name
values["developer"] = final_developer or None
@@ -288,9 +288,9 @@ def create_details_window(parent_widget, game_id=None):
with open(path, "r") as open_file:
data = json.loads(open_file.read())
data.update(values)
- save_games({game_id: data})
+ save_game(data)
else:
- save_games({game_id: values})
+ save_game(values)
parent_widget.update_games([game_id])
if parent_widget.stack.get_visible_child() == parent_widget.overview:
diff --git a/src/utils/heroic_parser.py b/src/utils/heroic_parser.py
index 8597f7f..84c2a7f 100644
--- a/src/utils/heroic_parser.py
+++ b/src/utils/heroic_parser.py
@@ -22,17 +22,12 @@ import json
import os
import time
-from gi.repository import GLib, Gtk
-from .create_dialog import create_dialog
-from .save_cover import save_cover
-
-
-def heroic_parser(parent_widget, action):
+def heroic_parser(parent_widget):
schema = parent_widget.schema
heroic_dir = os.path.expanduser(schema.get_string("heroic-location"))
- def heroic_not_found():
+ if not os.path.exists(os.path.join(heroic_dir, "config.json")):
if os.path.exists(
os.path.expanduser("~/.var/app/com.heroicgameslauncher.hgl/config/heroic/")
):
@@ -40,7 +35,6 @@ def heroic_parser(parent_widget, action):
"heroic-location",
"~/.var/app/com.heroicgameslauncher.hgl/config/heroic/",
)
- action(None, None)
elif os.path.exists(
os.path.join(
os.getenv("XDG_CONFIG_HOME")
@@ -56,49 +50,18 @@ def heroic_parser(parent_widget, action):
"heroic",
),
)
- action(None, None)
elif os.path.exists(os.path.join(os.getenv("appdata"), "heroic")):
schema.set_string(
"heroic-location", os.path.join(os.getenv("appdata"), "heroic")
)
- action(None, None)
else:
- filechooser = Gtk.FileDialog.new()
-
- def set_heroic_dir(_source, result, _unused):
- try:
- schema.set_string(
- "heroic-location",
- filechooser.select_folder_finish(result).get_path(),
- )
- action(None, None)
- except GLib.GError:
- return
-
- def choose_folder(_widget):
- filechooser.select_folder(parent_widget, None, set_heroic_dir, None)
-
- def response(widget, response):
- if response == "choose_folder":
- choose_folder(widget)
-
- create_dialog(
- parent_widget,
- _("Couldn't Import Games"),
- _("The Heroic directory cannot be found."),
- "choose_folder",
- _("Set Heroic Location"),
- ).connect("response", response)
-
- if not os.path.exists(os.path.join(heroic_dir, "config.json")):
- heroic_not_found()
- return {}
+ return
heroic_dir = os.path.expanduser(schema.get_string("heroic-location"))
-
- heroic_games = {}
current_time = int(time.time())
+ importer = parent_widget.importer
+
# Import Epic games
if not schema.get_boolean("heroic-import-epic"):
pass
@@ -114,6 +77,9 @@ def heroic_parser(parent_widget, action):
if not game["is_installed"]:
continue
+ importer.total_queue += 1
+ importer.queue += 1
+
values = {}
app_name = game["app_name"]
@@ -123,6 +89,7 @@ def heroic_parser(parent_widget, action):
values["game_id"] in parent_widget.games
and not parent_widget.games[values["game_id"]].removed
):
+ importer.save_game()
continue
values["name"] = game["title"]
@@ -145,9 +112,9 @@ def heroic_parser(parent_widget, action):
).hexdigest(),
)
if os.path.exists(image_path):
- save_cover(values, parent_widget, image_path)
+ importer.save_cover(values["game_id"], image_path)
- heroic_games[values["game_id"]] = values
+ importer.save_game(values)
except KeyError:
pass
@@ -160,6 +127,10 @@ def heroic_parser(parent_widget, action):
) as open_file:
data = open_file.read()
installed = json.loads(data)
+
+ importer.total_queue += len(installed["installed"])
+ importer.queue += len(installed["installed"])
+
for item in installed["installed"]:
values = {}
app_name = item["appName"]
@@ -170,6 +141,7 @@ def heroic_parser(parent_widget, action):
values["game_id"] in parent_widget.games
and not parent_widget.games[values["game_id"]].removed
):
+ importer.save_game()
continue
# Get game title and developer from library.json as they are not present in installed.json
@@ -188,7 +160,7 @@ def heroic_parser(parent_widget, action):
hashlib.sha256(game["art_square"].encode()).hexdigest(),
)
if os.path.exists(image_path):
- save_cover(values, parent_widget, image_path)
+ importer.save_cover(values["game_id"], image_path)
break
values["executable"] = (
@@ -201,7 +173,7 @@ def heroic_parser(parent_widget, action):
values["added"] = current_time
values["last_played"] = 0
- heroic_games[values["game_id"]] = values
+ importer.save_game(values)
# Import sideloaded games
if not schema.get_boolean("heroic-import-sideload"):
@@ -212,6 +184,10 @@ def heroic_parser(parent_widget, action):
) as open_file:
data = open_file.read()
library = json.loads(data)
+
+ importer.total_queue += len(library["games"])
+ importer.queue += len(library["games"])
+
for item in library["games"]:
values = {}
app_name = item["app_name"]
@@ -222,6 +198,7 @@ def heroic_parser(parent_widget, action):
values["game_id"] in parent_widget.games
and not parent_widget.games[values["game_id"]].removed
):
+ importer.save_game()
continue
values["name"] = item["title"]
@@ -240,28 +217,6 @@ def heroic_parser(parent_widget, action):
hashlib.sha256(item["art_square"].encode()).hexdigest(),
)
if os.path.exists(image_path):
- save_cover(values, parent_widget, image_path)
+ importer.save_cover(values["game_id"], image_path)
- heroic_games[values["game_id"]] = values
-
- if not heroic_games:
- create_dialog(
- parent_widget,
- _("No Games Found"),
- _("No new games were found in the Heroic library."),
- )
- elif len(heroic_games) == 1:
- create_dialog(
- parent_widget,
- _("Heroic Games Imported"),
- _("Successfully imported 1 game."),
- )
- elif len(heroic_games) > 1:
- games_no = str(len(heroic_games))
- create_dialog(
- parent_widget,
- _("Heroic Games Imported"),
- # The variable is the number of games
- _(f"Successfully imported {games_no} games."),
- )
- return heroic_games
+ importer.save_game(values)
diff --git a/src/utils/importer.py b/src/utils/importer.py
new file mode 100644
index 0000000..8a57b72
--- /dev/null
+++ b/src/utils/importer.py
@@ -0,0 +1,72 @@
+from gi.repository import Adw, Gtk
+
+from .create_dialog import create_dialog
+from .save_cover import save_cover
+from .save_game import save_game
+
+
+class Importer:
+ def __init__(self, parent_widget):
+ self.parent_widget = parent_widget
+ self.total_queue = 0
+ self.queue = 0
+ self.imported_no = 0
+ self.blocker = False
+
+ self.progressbar = Gtk.ProgressBar(margin_start=12, margin_end=12)
+ import_statuspage = Adw.StatusPage(
+ title=_("Importing Games…"),
+ child=self.progressbar,
+ )
+ self.import_dialog = Adw.Window(
+ content=import_statuspage,
+ modal=True,
+ default_width=350,
+ default_height=-1,
+ transient_for=parent_widget,
+ deletable=False,
+ )
+
+ self.import_dialog.present()
+
+ def save_cover(self, game_id, cover_path):
+ save_cover(self.parent_widget, game_id, cover_path)
+
+ def save_game(self, values=None):
+ if values:
+ self.imported_no += 1
+ save_game(values)
+ self.parent_widget.update_games([values["game_id"]])
+
+ self.queue -= 1
+ self.progressbar.set_fraction(1 - (self.queue / self.total_queue))
+
+ if self.queue == 0 and not self.blocker:
+ self.import_dialog.close()
+
+ def response(_widget, response):
+ if response == "open_preferences":
+ self.parent_widget.get_application().on_preferences_action(None)
+
+ if self.imported_no == 0:
+ create_dialog(
+ self.parent_widget,
+ _("No Games Found"),
+ _("No new games were found on your device."),
+ "open_preferences",
+ _("Preferences"),
+ ).connect("response", response)
+
+ elif self.imported_no == 1:
+ create_dialog(
+ self.parent_widget,
+ _("Game Imported"),
+ _("Successfully imported 1 game."),
+ )
+ elif self.imported_no > 1:
+ create_dialog(
+ self.parent_widget,
+ _("Games Imported"),
+ # The variable is the number of games
+ _(f"Successfully imported {self.imported_no} games."),
+ )
diff --git a/src/utils/save_cover.py b/src/utils/save_cover.py
index 70599ea..9777aa5 100644
--- a/src/utils/save_cover.py
+++ b/src/utils/save_cover.py
@@ -22,7 +22,7 @@ import os
from gi.repository import GdkPixbuf, Gio
-def save_cover(game, parent_widget, file_path, pixbuf=None, game_id=None):
+def save_cover(parent_widget, game_id, cover_path, pixbuf=None):
covers_dir = os.path.join(
os.getenv("XDG_DATA_HOME")
or os.path.expanduser(os.path.join("~", ".local", "share")),
@@ -30,18 +30,15 @@ def save_cover(game, parent_widget, file_path, pixbuf=None, game_id=None):
"covers",
)
- if game_id is None:
- game_id = game["game_id"]
+ if not os.path.exists(covers_dir):
+ os.makedirs(covers_dir)
if pixbuf is None:
- pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(file_path, 600, 900, False)
+ pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(cover_path, 600, 900, False)
def cover_callback(*_unused):
pass
- if not os.path.exists(covers_dir):
- os.makedirs(covers_dir)
-
open_file = Gio.File.new_for_path(os.path.join(covers_dir, f"{game_id}.tiff"))
parent_widget.pixbufs[game_id] = pixbuf
pixbuf.save_to_streamv_async(
diff --git a/src/utils/save_games.py b/src/utils/save_game.py
similarity index 81%
rename from src/utils/save_games.py
rename to src/utils/save_game.py
index 854c140..b93b28d 100644
--- a/src/utils/save_games.py
+++ b/src/utils/save_game.py
@@ -1,4 +1,4 @@
-# save_games.py
+# save_game.py
#
# Copyright 2022-2023 kramo
#
@@ -21,7 +21,7 @@ import json
import os
-def save_games(games):
+def save_game(game):
games_dir = os.path.join(
os.getenv("XDG_DATA_HOME")
or os.path.expanduser(os.path.join("~", ".local", "share")),
@@ -32,6 +32,5 @@ def save_games(games):
if not os.path.exists(games_dir):
os.makedirs(games_dir)
- for game in games:
- with open(os.path.join(games_dir, f"{game}.json"), "w") as open_file:
- open_file.write(json.dumps(games[game], indent=4, sort_keys=True))
+ with open(os.path.join(games_dir, f'{game["game_id"]}.json'), "w") as open_file:
+ open_file.write(json.dumps(game, indent=4, sort_keys=True))
diff --git a/src/utils/steam_parser.py b/src/utils/steam_parser.py
index 02d6133..299d3ba 100644
--- a/src/utils/steam_parser.py
+++ b/src/utils/steam_parser.py
@@ -23,11 +23,7 @@ import re
import time
import urllib.request
-from gi.repository import Adw, Gio, GLib, Gtk
-
-from .create_dialog import create_dialog
-from .save_cover import save_cover
-from .save_games import save_games
+from gi.repository import Gio, GLib
def update_values_from_data(content, values):
@@ -44,7 +40,9 @@ def update_values_from_data(content, values):
return values
-def get_game(task, datatypes, current_time, parent_widget, appmanifest, steam_dir):
+def get_game(
+ task, datatypes, current_time, parent_widget, appmanifest, steam_dir, importer
+):
values = {}
with open(appmanifest, "r") as open_file:
@@ -99,9 +97,8 @@ def get_game(task, datatypes, current_time, parent_widget, appmanifest, steam_di
f'{values["appid"]}_library_600x900.jpg',
)
):
- save_cover(
- values,
- parent_widget,
+ importer.save_cover(
+ values["game_id"],
os.path.join(
steam_dir,
"appcache",
@@ -114,93 +111,45 @@ def get_game(task, datatypes, current_time, parent_widget, appmanifest, steam_di
return
-def get_games_async(parent_widget, appmanifests, steam_dir, import_dialog, progressbar):
+def get_games_async(parent_widget, appmanifests, steam_dir, importer):
datatypes = ["appid", "name"]
current_time = int(time.time())
- steam_games = {}
- queue = 0
-
# Wrap the function in another one as Gio.Task.run_in_thread does not allow for passing args
def create_func(datatypes, current_time, parent_widget, appmanifest, steam_dir):
def wrapper(task, *_unused):
get_game(
- task, datatypes, current_time, parent_widget, appmanifest, steam_dir
+ task,
+ datatypes,
+ current_time,
+ parent_widget,
+ appmanifest,
+ steam_dir,
+ importer,
)
return wrapper
- def update_games(_task, result, parent_widget):
- nonlocal queue
- nonlocal total_queue
- nonlocal import_dialog
- nonlocal progressbar
-
- queue -= 1
- progressbar.set_fraction(1 - (queue / total_queue))
-
+ def update_games(_task, result):
try:
final_values = result.propagate_value()[1]
- steam_games[final_values["game_id"]] = final_values
- except (TypeError, GLib.GError):
- pass
+ # No need for an if statement as final_value would be None for games we don't want to save
+ importer.save_game(final_values)
+ except GLib.GError: # Handle the exception for the timeout
+ importer.save_game()
- if queue == 0:
- save_games(steam_games)
- parent_widget.update_games(steam_games)
- import_dialog.close()
- games_no = len(
- {
- game_id: final_values
- for game_id, final_values in steam_games.items()
- if "blacklisted" not in final_values.keys()
- }
- )
-
- def response(_widget, response):
- if response == "open_preferences":
- parent_widget.get_application().on_preferences_action(None)
-
- if games_no == 0:
- create_dialog(
- parent_widget,
- _("No Games Found"),
- _("No new games were found in the Steam library."),
- "open_preferences",
- _("Preferences"),
- ).connect("response", response)
-
- elif games_no == 1:
- create_dialog(
- parent_widget,
- _("Steam Games Imported"),
- _("Successfully imported 1 game."),
- )
- elif games_no > 1:
- games_no = str(games_no)
- create_dialog(
- parent_widget,
- _("Steam Games Imported"),
- # The variable is the number of games
- _(f"Successfully imported {games_no} games."),
- )
-
- total_queue = 0
for appmanifest in appmanifests:
- queue += 1
- total_queue += 1
-
cancellable = Gio.Cancellable.new()
GLib.timeout_add_seconds(5, cancellable.cancel)
- task = Gio.Task.new(None, cancellable, update_games, parent_widget)
+ task = Gio.Task.new(None, cancellable, update_games)
task.set_return_on_cancel(True)
task.run_in_thread(
create_func(datatypes, current_time, parent_widget, appmanifest, steam_dir)
)
-def steam_parser(parent_widget, action):
+def steam_parser(parent_widget):
schema = parent_widget.schema
steam_dir = os.path.expanduser(schema.get_string("steam-location"))
@@ -211,42 +160,14 @@ def steam_parser(parent_widget, action):
schema.set_string(
"steam-location", "~/.var/app/com.valvesoftware.Steam/data/Steam/"
)
- action(None, None)
elif os.path.exists(os.path.expanduser("~/.steam/steam/")):
schema.set_string("steam-location", "~/.steam/steam/")
- action(None, None)
elif os.path.exists(os.path.join(os.getenv("programfiles(x86)"), "Steam")):
schema.set_string(
"steam-location", os.path.join(os.getenv("programfiles(x86)"), "Steam")
)
- action(None, None)
else:
- filechooser = Gtk.FileDialog.new()
-
- def set_steam_dir(_source, result, _unused):
- try:
- schema.set_string(
- "steam-location",
- filechooser.select_folder_finish(result).get_path(),
- )
- action(None, None)
- except GLib.GError:
- return
-
- def choose_folder(_widget):
- filechooser.select_folder(parent_widget, None, set_steam_dir, None)
-
- def response(widget, response):
- if response == "choose_folder":
- choose_folder(widget)
-
- create_dialog(
- parent_widget,
- _("Couldn't Import Games"),
- _("The Steam directory cannot be found."),
- "choose_folder",
- _("Set Steam Location"),
- ).connect("response", response)
+ return
if os.path.exists(os.path.join(steam_dir, "steamapps")):
pass
@@ -256,28 +177,9 @@ def steam_parser(parent_widget, action):
schema.set_string("steam-location", os.path.join(steam_dir, "Steam"))
else:
steam_not_found()
- return {}
+ return
steam_dir = os.path.expanduser(schema.get_string("steam-location"))
-
- progressbar = Gtk.ProgressBar(margin_start=12, margin_end=12)
- import_statuspage = Adw.StatusPage(
- title=_("Importing Games…"),
- description=_("Talking to Steam"),
- child=progressbar,
- )
-
- import_dialog = Adw.Window(
- content=import_statuspage,
- modal=True,
- default_width=350,
- default_height=-1,
- transient_for=parent_widget,
- deletable=False,
- )
-
- import_dialog.present()
-
appmanifests = []
steam_dirs = schema.get_strv("steam-extra-dirs")
@@ -293,4 +195,8 @@ def steam_parser(parent_widget, action):
if os.path.isfile(path) and "appmanifest" in open_file:
appmanifests.append(path)
- get_games_async(parent_widget, appmanifests, directory, import_dialog, progressbar)
+ importer = parent_widget.importer
+ importer.total_queue += len(appmanifests)
+ importer.queue += len(appmanifests)
+
+ get_games_async(parent_widget, appmanifests, directory, importer)
diff --git a/src/window.py b/src/window.py
index 986f183..e69b168 100644
--- a/src/window.py
+++ b/src/window.py
@@ -24,7 +24,7 @@ from gi.repository import Adw, GdkPixbuf, Gio, GLib, Gtk
from .game import game
from .get_games import get_games
-from .save_games import save_games
+from .save_game import save_game
@Gtk.Template(resource_path="/hu/kramo/Cartridges/gtk/window.ui")
@@ -422,7 +422,7 @@ class CartridgesWindow(Adw.ApplicationWindow):
return
data = get_games([game_id])[game_id]
data.pop("removed")
- save_games({game_id: data})
+ save_game(data)
self.update_games([game_id])
self.toasts[game_id].dismiss()
self.toasts.pop(game_id)