Add project files

This commit is contained in:
kra-mo
2022-12-26 14:38:37 +01:00
parent e90a79778c
commit f5c1012628
39 changed files with 2588 additions and 0 deletions

View File

@@ -0,0 +1,194 @@
# create_details_window.py
#
# Copyright 2022 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
def create_details_window(parent_widget, game_id = None):
import os, json, time
from gi.repository import Adw, Gtk, GLib, GdkPixbuf
from .create_dialog import create_dialog
from .save_games import save_games
from .save_cover import save_cover
window = Adw.Window.new()
games = parent_widget.games
pixbuf = None
if game_id == None:
window.set_title(_("Add New Game"))
cover = Gtk.Picture.new_for_pixbuf(parent_widget.placeholder_pixbuf)
name = Gtk.Entry.new()
executable = Gtk.Entry.new()
apply_button = Gtk.Button.new_with_label(_("Confirm"))
else:
window.set_title(_("Edit Game Details"))
cover = Gtk.Picture.new_for_pixbuf((parent_widget.visible_widgets | parent_widget.hidden_widgets)[game_id].pixbuf)
name = Gtk.Entry.new_with_buffer(Gtk.EntryBuffer.new(games[game_id]["name"], -1))
executable = Gtk.Entry.new_with_buffer(Gtk.EntryBuffer.new((games[game_id]["executable"]), -1))
apply_button = Gtk.Button.new_with_label(_("Apply"))
file_filter = Gtk.FileFilter.new()
file_filter.set_name(_("Images"))
file_filter.add_pixbuf_formats()
filechooser = Gtk.FileDialog.new()
filechooser.set_current_filter(file_filter)
cover.add_css_class("card")
cover.set_size_request(200, 300)
cover_button = Gtk.Button.new_from_icon_name("document-edit-symbolic")
cover_button.set_halign(Gtk.Align.END)
cover_button.set_valign(Gtk.Align.END)
cover_button.set_margin_bottom(6)
cover_button.set_margin_end(6)
cover_button.add_css_class("circular")
cover_button.add_css_class("osd")
cover_overlay = Gtk.Overlay.new()
cover_overlay.set_child(cover)
cover_overlay.add_overlay(cover_button)
cover_overlay.set_halign(Gtk.Align.CENTER)
cover_overlay.set_valign(Gtk.Align.CENTER)
cover_group = Adw.PreferencesGroup.new()
cover_group.add(cover_overlay)
title_group = Adw.PreferencesGroup.new()
title_group.set_title(_("Title"))
title_group.set_description(_("The title of the game"))
title_group.add(name)
exec_group = Adw.PreferencesGroup.new()
exec_group.set_title(_("Executable"))
exec_group.set_description(_("File to open or command to run when launching the game"))
exec_group.add(executable)
general_page = Adw.PreferencesPage.new()
general_page.add(cover_group)
general_page.add(title_group)
general_page.add(exec_group)
cancel_button = Gtk.Button.new_with_label(_("Cancel"))
apply_button.add_css_class("suggested-action")
header_bar = Adw.HeaderBar.new()
header_bar.set_show_start_title_buttons(False)
header_bar.set_show_end_title_buttons(False)
header_bar.pack_start(cancel_button)
header_bar.pack_end(apply_button)
main_box = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
main_box.append(header_bar)
main_box.append(general_page)
window.set_modal(True)
window.set_default_size(500, 650)
window.set_content(main_box)
window.set_transient_for(parent_widget)
def choose_cover(widget):
filechooser.open(window, None, None, set_cover, None)
def set_cover(source, result, _):
nonlocal pixbuf
try:
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(filechooser.open_finish(result).get_path(), 200, 300, False)
cover.set_pixbuf(pixbuf)
except GLib.GError:
return
def close_window(widget, callback=None):
window.close()
def apply_preferences(widget, callback=None):
nonlocal pixbuf
nonlocal game_id
values = {}
games_dir = os.path.join(os.environ.get("XDG_DATA_HOME"), "games")
final_name = name.get_buffer().get_text()
final_executable = executable.get_buffer().get_text()
if game_id == None:
if final_name == "":
create_dialog(window, _("Couldn't Add Game"), _("Game title cannot be empty."))
return
if final_executable == "":
create_dialog(window, _("Couldn't Add Game"), _("Executable cannot be empty."))
return
numbers = [0]
for game in games:
if "imported_" in game:
numbers.append(int(game.replace("imported_", "")))
game_id = "imported_" + str(max(numbers)+1)
games[game_id] = {}
values["game_id"] = game_id
values["hidden"] = False
values["source"] = "imported"
values["added"] = int(time.time())
values["last_played"] = 0
else:
if final_name == "":
create_dialog(window, _("Couldn't Apply Preferences"), _("Game title cannot be empty."))
return
if final_executable == "":
create_dialog(window, _("Couldn't Apply Preferences"), _("Executable cannot be empty."))
return
if pixbuf != None:
values["pixbuf_options"] = save_cover(None, parent_widget, None, pixbuf, game_id)
values["name"] = final_name
values["executable"] = final_executable
games[game_id].update(values)
save_games(games)
parent_widget.update_games([game_id])
if parent_widget.stack.get_visible_child() == parent_widget.overview:
parent_widget.show_overview(None, game_id)
window.close()
parent_widget.show_overview(None, game_id)
def focus_executable(widget):
window.set_focus(executable)
cover_button.connect("clicked", choose_cover)
cancel_button.connect("clicked", close_window)
apply_button.connect("clicked", apply_preferences)
name.connect("activate", focus_executable)
executable.connect("activate", apply_preferences)
shortcut_controller = Gtk.ShortcutController.new()
shortcut_controller.add_shortcut(Gtk.Shortcut.new(Gtk.ShortcutTrigger.parse_string('Escape'), Gtk.CallbackAction.new(close_window)))
window.add_controller(shortcut_controller)
window.set_focus(name)
window.present()

View File

@@ -0,0 +1,30 @@
# create_dialog.py
#
# Copyright 2022 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
def create_dialog(parent_widget, heading, body, extra_option=None, extra_label=None):
from gi.repository import Adw, Gtk
dialog = Adw.MessageDialog.new(parent_widget, _(heading), body)
dialog.add_response("dismiss", _("Dismiss"))
if extra_option:
dialog.add_response(extra_option, _(extra_label))
Gtk.Window.present(dialog)
return dialog

35
src/utils/get_cover.py Normal file
View File

@@ -0,0 +1,35 @@
# get_cover.py
#
# Copyright 2022 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
def get_cover(game, parent_widget):
from gi.repository import GdkPixbuf
import os
cover_path = os.path.join(os.environ.get("XDG_DATA_HOME"), "covers", game["game_id"] + ".dat")
if os.path.isfile(cover_path) == False:
return parent_widget.placeholder_pixbuf
open_file = open((cover_path), "rb")
data = open_file.read()
open_file.close()
try:
return GdkPixbuf.Pixbuf.new_from_data(data, *parent_widget.games[game["game_id"]]["pixbuf_options"])
except KeyError:
return parent_widget.placeholder_pixbuf

34
src/utils/get_games.py Normal file
View File

@@ -0,0 +1,34 @@
# get_games.py
#
# Copyright 2022 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
def get_games():
import os, json
games_dir = os.path.join(os.environ.get("XDG_DATA_HOME"), "games")
games = {}
if os.path.exists(games_dir) == False:
return {}
for game in os.listdir(games_dir):
open_file = open(os.path.join(games_dir, game), "r")
data = json.loads(open_file.read())
open_file.close()
games[data["game_id"]] = data
return games

28
src/utils/run_command.py Normal file
View File

@@ -0,0 +1,28 @@
# run_command.py
#
# Copyright 2022 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
def run_command(parent_widget, executable):
import subprocess, sys
from gi.repository import Gio
subprocess.Popen(["flatpak-spawn --host " + executable], shell=True, start_new_session=True)
if Gio.Settings.new("hu.kramo.GameShelf").get_boolean("exit-after-launch") == True:
sys.exit()

39
src/utils/save_cover.py Normal file
View File

@@ -0,0 +1,39 @@
# save_cover.py
#
# Copyright 2022 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
def save_cover(game, parent_widget, file_path, pixbuf = None, game_id = None):
from gi.repository import GdkPixbuf
import os
covers_dir = os.path.join(os.environ.get("XDG_DATA_HOME"), "covers")
if os.path.exists(covers_dir) == False:
os.makedirs(covers_dir)
if game_id == None:
game_id = game["game_id"]
cover_path = os.path.join(covers_dir, game_id + ".dat")
if pixbuf == None:
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(file_path, 200, 300, False)
open_file = open((cover_path), "wb")
open_file.write(bytes(pixbuf.get_pixels()))
open_file.close()
return [pixbuf.get_colorspace(), pixbuf.get_has_alpha(), pixbuf.get_bits_per_sample(), pixbuf.get_width(), pixbuf.get_height(), pixbuf.get_rowstride()]

31
src/utils/save_games.py Normal file
View File

@@ -0,0 +1,31 @@
# save_games.py
#
# Copyright 2022 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
def save_games(games):
import os, json
games_dir = os.path.join(os.environ.get("XDG_DATA_HOME"), "games")
existing = []
if os.path.exists(games_dir) == False:
os.makedirs(games_dir)
for game in games:
open_file = open(os.path.join(games_dir, game + ".json"), "w")
open_file.write(json.dumps(games[game], indent=4))
open_file.close()

103
src/utils/steam_parser.py Normal file
View File

@@ -0,0 +1,103 @@
# steam_parser.py
#
# Copyright 2022 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
def steam_parser(parent_widget, action):
import os, re, time
from gi.repository import Gtk, GLib
from .create_dialog import create_dialog
from .save_cover import save_cover
schema = parent_widget.schema
steam_dir = os.path.expanduser(os.path.join(schema.get_string("steam-location")))
def steam_not_found():
filechooser = Gtk.FileDialog.new()
def set_steam_dir(source, result, _):
try:
schema.set_string("steam-location", filechooser.select_folder_finish(result).get_path())
steam_dir = steam_dir = os.path.join(schema.get_string("steam-location"))
action(None, None)
except GLib.GError:
return
def choose_folder(widget):
filechooser.select_folder(parent_widget, None, None, set_steam_dir, None)
def response(widget, response):
if response == "choose_folder":
choose_folder(widget)
create_dialog(parent_widget, _("Couldn't Import Games"), _("Steam directory cannot be found."), "choose_folder", _("Set Steam Location")).connect("response", response)
if os.path.exists(os.path.join(steam_dir, "steamapps")) == True:
pass
elif os.path.exists(os.path.join(steam_dir, "steam", "steamapps")) == True:
schema.set_string("steam-location", os.path.join(steam_dir, "steam"))
elif os.path.exists(os.path.join(steam_dir, "Steam", "steamapps")) == True:
schema.set_string("steam-location", os.path.join(steam_dir, "Steam"))
else:
steam_not_found()
return {}
steam_dir = os.path.join(schema.get_string("steam-location"))
appmanifests = []
datatypes = ["appid", "name"]
steam_games = {}
current_time = int(time.time())
for file in os.listdir(os.path.join(steam_dir, "steamapps")):
path = os.path.join(steam_dir, "steamapps", file)
if os.path.isfile(path) and "appmanifest" in file:
appmanifests.append(path)
for appmanifest in appmanifests:
values = {}
file = open(appmanifest, "r")
data = file.read()
file.close()
for datatype in datatypes:
value = re.findall("\"" + datatype + "\"\t\t\"(.*)\"\n", data)
values [datatype] = value[0]
values["game_id"] = "steam_" + values["appid"]
if values["game_id"] in parent_widget.games and "removed" not in parent_widget.games[values["game_id"]].keys():
continue
values["executable"] = "xdg-open steam://rungameid/" + values["appid"]
values["hidden"] = False
values["source"] = "steam"
values["added"] = current_time
values["last_played"] = 0
if os.path.isfile(os.path.join(steam_dir, "appcache", "librarycache", values["appid"] + "_library_600x900.jpg")) == True:
values["pixbuf_options"] = save_cover(values, parent_widget, os.path.join(steam_dir, "appcache", "librarycache", values["appid"] + "_library_600x900.jpg"))
steam_games[values["game_id"]] = values
if len(steam_games) == 0:
create_dialog(parent_widget, _("No Games Found"), _("No new games found in Steam library."))
elif len(steam_games) == 1:
create_dialog(parent_widget, _("Steam Games Imported"), _("Successfully imported 1 game."))
elif len(steam_games) > 1:
create_dialog(parent_widget, _("Steam Games Imported"), _("Successfully imported ") + str(len(steam_games)) + _(" games."))
return steam_games

View File

@@ -0,0 +1,33 @@
# toggle_hidden.py
#
# Copyright 2022 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
def toggle_hidden(game):
import os, json
games_dir = os.path.join(os.environ.get("XDG_DATA_HOME"), "games")
if os.path.exists(games_dir) == False:
return
file = open(os.path.join(games_dir, game + ".json"), "r")
data = json.loads(file.read())
file.close()
file = open(os.path.join(games_dir, game + ".json"), "w")
data["hidden"] = not data["hidden"]
file.write(json.dumps(data, indent=4))
file.close()