diff --git a/data/gtk/preferences.blp b/data/gtk/preferences.blp
index 9654c29..543a993 100644
--- a/data/gtk/preferences.blp
+++ b/data/gtk/preferences.blp
@@ -85,6 +85,19 @@ template $PreferencesWindow : Adw.PreferencesWindow {
title: _("Import");
icon-name: "document-save-symbolic";
+ Adw.PreferencesGroup import_behavior_group {
+ title: _("Behavior");
+
+ Adw.ActionRow {
+ title: _("Remove Missing Games");
+ activatable-widget: remove_missing_switch;
+
+ Switch remove_missing_switch {
+ valign: center;
+ }
+ }
+ }
+
Adw.PreferencesGroup sources_group {
title: _("Sources");
diff --git a/data/hu.kramo.Cartridges.gschema.xml.in b/data/hu.kramo.Cartridges.gschema.xml.in
index d31c2ed..9003a93 100644
--- a/data/hu.kramo.Cartridges.gschema.xml.in
+++ b/data/hu.kramo.Cartridges.gschema.xml.in
@@ -10,6 +10,9 @@
false
+
+ true
+
true
diff --git a/src/importer/importer.py b/src/importer/importer.py
index 756ea3b..f3d95cb 100644
--- a/src/importer/importer.py
+++ b/src/importer/importer.py
@@ -49,8 +49,15 @@ class Importer(ErrorProducer):
n_pipelines_done: int = 0
game_pipelines: set[Pipeline] = None
+ removed_games: set[Game] = set()
+
def __init__(self):
super().__init__()
+
+ # TODO: make this stateful
+ shared.store.new_game_ids = set()
+ shared.store.duplicate_game_ids = set()
+
self.game_pipelines = set()
self.sources = set()
@@ -217,9 +224,36 @@ class Importer(ErrorProducer):
if self.finished:
self.import_callback()
+ def remove_games(self):
+ """Set removed to True for missing games"""
+ if not shared.schema.get_boolean("remove-missing"):
+ return
+
+ for game in shared.store:
+ if game.removed:
+ continue
+ if game.source == "imported":
+ continue
+ if not shared.schema.get_boolean(game.base_source):
+ continue
+ if game.game_id in shared.store.duplicate_game_ids:
+ continue
+ if game.game_id in shared.store.new_game_ids:
+ continue
+
+ logging.debug("Removing missing game %s (%s)", game.name, game.game_id)
+
+ game.removed = True
+ game.save()
+ game.update()
+ self.removed_games.add(game)
+
def import_callback(self):
"""Callback called when importing has finished"""
logging.info("Import done")
+ self.remove_games()
+ shared.store.new_game_ids = set()
+ shared.store.duplicate_game_ids = set()
self.import_dialog.close()
self.summary_toast = self.create_summary_toast()
self.create_error_dialog()
diff --git a/src/preferences.py b/src/preferences.py
index 6b7d105..a3172ac 100644
--- a/src/preferences.py
+++ b/src/preferences.py
@@ -52,6 +52,8 @@ class PreferencesWindow(Adw.PreferencesWindow):
cover_launches_game_switch = Gtk.Template.Child()
high_quality_images_switch = Gtk.Template.Child()
+ remove_missing_switch = Gtk.Template.Child()
+
steam_expander_row = Gtk.Template.Child()
steam_data_action_row = Gtk.Template.Child()
steam_data_file_chooser_button = Gtk.Template.Child()
@@ -184,6 +186,7 @@ class PreferencesWindow(Adw.PreferencesWindow):
"exit-after-launch",
"cover-launches-game",
"high-quality-images",
+ "remove-missing",
"lutris-import-steam",
"lutris-import-flatpak",
"heroic-import-epic",
diff --git a/src/store/store.py b/src/store/store.py
index 231bbdc..0e07749 100644
--- a/src/store/store.py
+++ b/src/store/store.py
@@ -18,7 +18,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later
import logging
-from typing import MutableMapping, Generator, Any
+from typing import Any, Generator, MutableMapping
from src import shared
from src.game import Game
@@ -33,12 +33,16 @@ class Store:
pipeline_managers: set[Manager]
pipelines: dict[str, Pipeline]
source_games: MutableMapping[str, MutableMapping[str, Game]]
+ new_game_ids: set[str]
+ duplicate_game_ids: set[str]
def __init__(self) -> None:
self.managers = {}
self.pipeline_managers = set()
self.pipelines = {}
self.source_games = {}
+ self.new_game_ids = set()
+ self.duplicate_game_ids = set()
def __contains__(self, obj: object) -> bool:
"""Check if the game is present in the store with the `in` keyword"""
@@ -114,6 +118,7 @@ class Store:
if not stored_game:
# New game, do as normal
logging.debug("New store game %s (%s)", game.name, game.game_id)
+ self.new_game_ids.add(game.game_id)
elif stored_game.removed:
# Will replace a removed game, cleanup its remains
logging.debug(
@@ -125,6 +130,7 @@ class Store:
else:
# Duplicate game, ignore it
logging.debug("Duplicate store game %s (%s)", game.name, game.game_id)
+ self.duplicate_game_ids.add(game.game_id)
return None
# Connect signals