Added Lutris import

This commit is contained in:
kramo
2023-03-31 15:22:45 +02:00
parent a53e49610c
commit 780fd0a09f
12 changed files with 241 additions and 10 deletions

View File

@@ -108,6 +108,30 @@ template PreferencesWindow : Adw.PreferencesWindow {
} }
} }
Adw.ExpanderRow lutris_expander_row {
title: _("Lutris");
show-enable-switch: true;
Adw.ActionRow {
title: _("Lutris Install Location");
subtitle: _("Directory to use when importing games");
Button lutris_file_chooser_button {
icon-name: "folder-symbolic";
valign: center;
}
}
Adw.ActionRow {
title: _("Lutris Cache Location");
subtitle: _("Directory to use when importing game covers");
Button lutris_cache_file_chooser_button {
icon-name: "folder-symbolic";
valign: center;
}
}
}
Adw.ExpanderRow heroic_expander_row { Adw.ExpanderRow heroic_expander_row {
title: _("Heroic"); title: _("Heroic");
show-enable-switch: true; show-enable-switch: true;

View File

@@ -19,6 +19,15 @@
<key name="steam-extra-dirs" type="as"> <key name="steam-extra-dirs" type="as">
<default>[]</default> <default>[]</default>
</key> </key>
<key name="lutris" type="b">
<default>true</default>
</key>
<key name="lutris-location" type="s">
<default>"~/.var/app/net.lutris.Lutris/data/lutris/"</default>
</key>
<key name="lutris-cache-location" type="s">
<default>"~/.var/app/net.lutris.Lutris/cache/lutris"</default>
</key>
<key name="heroic" type="b"> <key name="heroic" type="b">
<default>true</default> <default>true</default>
</key> </key>

View File

@@ -13,9 +13,12 @@
"--talk-name=org.gtk.vfs.*", "--talk-name=org.gtk.vfs.*",
"--filesystem=xdg-run/gvfsd", "--filesystem=xdg-run/gvfsd",
"--filesystem=~/.steam/steam/:ro", "--filesystem=~/.steam/steam/:ro",
"--filesystem=xdg-data/lutris/:ro",
"--filesystem=xdg-cache/lutris/:ro",
"--filesystem=xdg-config/heroic/:ro", "--filesystem=xdg-config/heroic/:ro",
"--filesystem=xdg-data/bottles/:ro", "--filesystem=xdg-data/bottles/:ro",
"--filesystem=~/.var/app/com.valvesoftware.Steam/data/Steam/:ro", "--filesystem=~/.var/app/com.valvesoftware.Steam/data/Steam/:ro",
"--filesystem=~/.var/app/net.lutris.Lutris/:ro",
"--filesystem=~/.var/app/com.heroicgameslauncher.hgl/config/heroic/:ro", "--filesystem=~/.var/app/com.heroicgameslauncher.hgl/config/heroic/:ro",
"--filesystem=~/.var/app/com.usebottles.bottles/data/bottles/:ro" "--filesystem=~/.var/app/com.usebottles.bottles/data/bottles/:ro"
], ],

View File

@@ -33,6 +33,7 @@ from .create_details_window import create_details_window
from .get_games import get_games from .get_games import get_games
from .heroic_parser import heroic_parser from .heroic_parser import heroic_parser
from .importer import Importer from .importer import Importer
from .lutris_parser import lutris_parser
from .preferences import PreferencesWindow from .preferences import PreferencesWindow
from .save_game import save_game from .save_game import save_game
from .steam_parser import steam_parser from .steam_parser import steam_parser
@@ -168,6 +169,9 @@ class CartridgesApplication(Adw.Application):
if self.win.schema.get_boolean("steam"): if self.win.schema.get_boolean("steam"):
steam_parser(self.win) steam_parser(self.win)
if self.win.schema.get_boolean("lutris"):
lutris_parser(self.win)
if self.win.schema.get_boolean("heroic"): if self.win.schema.get_boolean("heroic"):
heroic_parser(self.win) heroic_parser(self.win)

View File

@@ -26,6 +26,7 @@ cartridges_sources = [
'utils/steam_parser.py', 'utils/steam_parser.py',
'utils/heroic_parser.py', 'utils/heroic_parser.py',
'utils/bottles_parser.py', 'utils/bottles_parser.py',
'utils/lutris_parser.py',
'utils/get_games.py', 'utils/get_games.py',
'utils/save_game.py', 'utils/save_game.py',
'utils/save_cover.py', 'utils/save_cover.py',

View File

@@ -98,6 +98,10 @@ class PreferencesWindow(Adw.PreferencesWindow):
steam_clear_button_revealer = Gtk.Template.Child() steam_clear_button_revealer = Gtk.Template.Child()
steam_clear_button = Gtk.Template.Child() steam_clear_button = Gtk.Template.Child()
lutris_expander_row = Gtk.Template.Child()
lutris_file_chooser_button = Gtk.Template.Child()
lutris_cache_file_chooser_button = Gtk.Template.Child()
heroic_expander_row = Gtk.Template.Child() heroic_expander_row = Gtk.Template.Child()
heroic_file_chooser_button = Gtk.Template.Child() heroic_file_chooser_button = Gtk.Template.Child()
heroic_epic_switch = Gtk.Template.Child() heroic_epic_switch = Gtk.Template.Child()
@@ -191,6 +195,45 @@ class PreferencesWindow(Adw.PreferencesWindow):
) )
self.steam_clear_button.connect("clicked", clear_steam_dirs) self.steam_clear_button.connect("clicked", clear_steam_dirs)
# Lutris
ImportPreferences(
self,
"lutris",
"Lutris",
"lutris-location",
["pga.db"],
self.lutris_expander_row,
self.lutris_file_chooser_button,
)
def set_cache_dir(_source, result, _unused):
try:
path = self.file_chooser.select_folder_finish(result).get_path()
def response(widget, response):
if response == "choose_folder":
self.choose_folder(widget, set_cache_dir)
if not os.path.exists(os.path.join(path, "coverart")):
create_dialog(
self.parent_widget,
_("Cache Not Found"),
_("Select the Lutris cache directory."),
"choose_folder",
_("Set Location"),
).connect("response", response)
else:
self.schema.set_string(
"lutris-cache-location",
path,
)
except GLib.GError:
pass
self.lutris_cache_file_chooser_button.connect(
"clicked", self.choose_folder, set_cache_dir
)
# Heroic # Heroic
ImportPreferences( ImportPreferences(
self, self,

View File

@@ -18,7 +18,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
import os import os
import time from time import time
import yaml import yaml
@@ -53,7 +53,7 @@ def bottles_parser(parent_widget):
return return
bottles_dir = os.path.expanduser(schema.get_string("bottles-location")) bottles_dir = os.path.expanduser(schema.get_string("bottles-location"))
current_time = int(time.time()) current_time = int(time())
with open(os.path.join(bottles_dir, "library.yml"), "r") as open_file: with open(os.path.join(bottles_dir, "library.yml"), "r") as open_file:
data = open_file.read() data = open_file.read()

View File

@@ -20,7 +20,7 @@
import hashlib import hashlib
import json import json
import os import os
import time from time import time
def heroic_parser(parent_widget): def heroic_parser(parent_widget):
@@ -60,7 +60,7 @@ def heroic_parser(parent_widget):
return return
heroic_dir = os.path.expanduser(schema.get_string("heroic-location")) heroic_dir = os.path.expanduser(schema.get_string("heroic-location"))
current_time = int(time.time()) current_time = int(time())
importer = parent_widget.importer importer = parent_widget.importer

149
src/utils/lutris_parser.py Normal file
View File

@@ -0,0 +1,149 @@
# lutris_parser.py
#
# Copyright 2022-2023 kramo
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# SPDX-License-Identifier: GPL-3.0-or-later
import os
import shutil
from sqlite3 import connect
from time import time
def lutris_parser(parent_widget):
schema = parent_widget.schema
database_path = os.path.join(
os.path.expanduser(schema.get_string("lutris-location")), "pga.db"
)
if not os.path.isfile(database_path):
if os.path.exists(
os.path.expanduser("~/.var/app/net.lutris.Lutris/data/lutris/")
):
schema.set_string(
"lutris-location", "~/.var/app/net.lutris.Lutris/data/lutris/"
)
elif os.path.exists(
os.path.join(
os.getenv("XDG_DATA_HOME")
or os.path.expanduser(os.path.join("~", ".local", "share")),
"lutris",
)
):
schema.set_string(
"lutris-location",
os.path.join(
os.getenv("XDG_DATA_HOME")
or os.path.expanduser(os.path.join("~", ".local", "share")),
"lutris",
),
)
else:
return
cache_dir = os.path.expanduser(schema.get_string("lutris-cache-location"))
if not os.path.exists(cache_dir):
if os.path.exists(
os.path.expanduser("~/.var/app/net.lutris.Lutris/cache/lutris/")
):
schema.set_string(
"lutris-cache-location", "~/.var/app/net.lutris.Lutris/cache/lutris/"
)
elif os.path.exists(
os.path.join(
os.getenv("XDG_CACHE_HOME")
or os.path.expanduser(os.path.join("~", ".cache")),
"lutris",
)
):
schema.set_string(
"lutris-cache-location",
os.path.join(
os.getenv("XDG_CACHE_HOME")
or os.path.expanduser(os.path.join("~", ".cache")),
"lutris",
),
)
else:
return
database_path = os.path.join(
os.path.expanduser(schema.get_string("lutris-location")), "pga.db"
)
cache_dir = os.path.expanduser(schema.get_string("lutris-cache-location"))
db_cache_dir = os.path.join(
os.getenv("XDG_CACHE_HOME") or os.path.expanduser(os.path.join("~", ".cache")),
"cartridges",
"lutris",
)
os.makedirs(db_cache_dir, exist_ok=True)
shutil.copyfile(database_path, os.path.join(db_cache_dir, "pga.db"))
db_request = """
SELECT
id, name, slug, runner, hidden
FROM
'games'
WHERE
name IS NOT NULL
AND slug IS NOT NULL
AND configPath IS NOT NULL
AND installed IS TRUE
;
"""
connection = connect(os.path.join(db_cache_dir, "pga.db"))
cursor = connection.execute(db_request)
rows = cursor.fetchall()
connection.close()
if schema.get_boolean("steam"):
rows = [row for row in rows if not row[3] == "steam"]
current_time = int(time())
importer = parent_widget.importer
importer.total_queue += len(rows)
importer.queue += len(rows)
for row in rows:
values = {}
values["game_id"] = f"lutris_{row[3]}_{row[0]}"
if (
values["game_id"] in parent_widget.games
and not parent_widget.games[values["game_id"]].removed
):
importer.save_game()
continue
values["added"] = current_time
values["executable"] = ["xdg-open", f"lutris:rungameid/{row[0]}"]
values["hidden"] = row[4] == 1
values["last_played"] = 0
values["name"] = row[1]
values["source"] = f"lutris_{row[3]}"
if os.path.isfile(os.path.join(cache_dir, "coverart", f"{row[2]}.jpg")):
importer.save_cover(
values["game_id"], os.path.join(cache_dir, "coverart", f"{row[2]}.jpg")
)
importer.save_game(values)

View File

@@ -30,8 +30,7 @@ def save_cover(parent_widget, game_id, cover_path, pixbuf=None):
"covers", "covers",
) )
if not os.path.exists(covers_dir): os.makedirs(covers_dir, exist_ok=True)
os.makedirs(covers_dir)
if pixbuf is None: if pixbuf is None:
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(cover_path, 400, 600, False) pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(cover_path, 400, 600, False)

View File

@@ -29,8 +29,7 @@ def save_game(game):
"games", "games",
) )
if not os.path.exists(games_dir): os.makedirs(games_dir, exist_ok=True)
os.makedirs(games_dir)
with open(os.path.join(games_dir, f'{game["game_id"]}.json'), "w") as open_file: 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)) open_file.write(json.dumps(game, indent=4, sort_keys=True))

View File

@@ -20,8 +20,8 @@
import json import json
import os import os
import re import re
import time
import urllib.request import urllib.request
from time import time
from gi.repository import Gio, GLib from gi.repository import Gio, GLib
@@ -113,7 +113,7 @@ def get_game(
def get_games_async(parent_widget, appmanifests, steam_dir, importer): def get_games_async(parent_widget, appmanifests, steam_dir, importer):
datatypes = ["appid", "name"] datatypes = ["appid", "name"]
current_time = int(time.time()) current_time = int(time())
# Wrap the function in another one as Gio.Task.run_in_thread does not allow for passing args # 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 create_func(datatypes, current_time, parent_widget, appmanifest, steam_dir):