games: Use Python's JSON module

This commit is contained in:
Jamie Gravendeel
2025-11-29 18:53:34 +01:00
parent a3fef27505
commit b11c1b314f
2 changed files with 62 additions and 25 deletions

View File

@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: Copyright 2025 Zoey Ahmed # SPDX-FileCopyrightText: Copyright 2025 Zoey Ahmed
# SPDX-FileCopyrightText: Copyright 2025 Jamie Gravendeel
import gettext import gettext
import locale import locale
@@ -12,13 +13,14 @@ import gi
gi.require_versions({ gi.require_versions({
"Gtk": "4.0", "Gtk": "4.0",
"Adw": "1", "Adw": "1",
"Json": "1.0",
}) })
from gi.repository import Gio from gi.repository import Gio, GLib
from .config import LOCALEDIR, PKGDATADIR from .config import LOCALEDIR, PKGDATADIR
DATA_DIR = Path(GLib.get_user_data_dir(), "cartridges")
_RESOURCES = ("data", "icons", "ui") _RESOURCES = ("data", "icons", "ui")
signal.signal(signal.SIGINT, signal.SIG_DFL) signal.signal(signal.SIGINT, signal.SIG_DFL)

View File

@@ -1,22 +1,35 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: Copyright 2025 Zoey Ahmed # SPDX-FileCopyrightText: Copyright 2025 Zoey Ahmed
# SPDX-FileCopyrightText: Copyright 2025 kramo # SPDX-FileCopyrightText: Copyright 2025 kramo
# SPDX-FileCopyrightText: Copyright 2025 Jamie Gravendeel
import json
from collections.abc import Generator from collections.abc import Generator
from pathlib import Path from json import JSONDecodeError
from typing import cast from types import UnionType
from typing import Any
from gi.repository import ( from gi.repository import Gdk, Gio, GLib, GObject
Gdk,
Gio,
GLib,
GObject,
Json, # pyright: ignore[reportAttributeAccessIssue]
)
DATA_DIR = Path(GLib.get_user_data_dir(), "cartridges") from cartridges import DATA_DIR
GAMES_DIR = DATA_DIR / "games"
COVERS_DIR = DATA_DIR / "covers" _GAMES_DIR = DATA_DIR / "games"
_COVERS_DIR = DATA_DIR / "covers"
_SPEC_VERSION = 2.0
_PROPERTIES: dict[str, tuple[type | UnionType, bool]] = {
"added": (int, False),
"executable": (str | list[str], True),
"game_id": (str, True),
"source": (str, True),
"hidden": (bool, False),
"last_played": (int, False),
"name": (str, True),
"developer": (str, False),
"removed": (bool, False),
"blacklisted": (bool, False),
"version": (float, False),
}
class Game(GObject.Object): class Game(GObject.Object):
@@ -29,30 +42,52 @@ class Game(GObject.Object):
game_id = GObject.Property(type=str) game_id = GObject.Property(type=str)
source = GObject.Property(type=str) source = GObject.Property(type=str)
hidden = GObject.Property(type=bool, default=False) hidden = GObject.Property(type=bool, default=False)
last_played = GObject.Property(type=int, default=0) last_played = GObject.Property(type=int)
name = GObject.Property(type=str) name = GObject.Property(type=str)
developer = GObject.Property(type=str) developer = GObject.Property(type=str)
removed = GObject.Property(type=bool, default=False) removed = GObject.Property(type=bool, default=False)
blacklisted = GObject.Property(type=bool, default=False) blacklisted = GObject.Property(type=bool, default=False)
version = GObject.Property(type=float, default=2.0) version = GObject.Property(type=float, default=_SPEC_VERSION)
cover = GObject.Property(type=Gdk.Texture) cover = GObject.Property(type=Gdk.Texture)
def __init__(self, data: dict[str, Any]):
super().__init__()
def load() -> Generator[Game]: for name, (type_, required) in _PROPERTIES.items():
"""Load the user's games from disk.""" value = data.get(name)
for path in GAMES_DIR.glob("*.json"):
if not required and value is None:
continue
if not isinstance(value, type_):
raise TypeError
match name:
case "executable" if isinstance(value, list):
value = " ".join(value)
case "version" if value and value > _SPEC_VERSION:
raise TypeError
case "version":
continue
setattr(self, name, value)
def _load() -> Generator[Game]:
for path in _GAMES_DIR.glob("*.json"):
try: try:
data = path.read_text("utf-8") with path.open(encoding="utf-8") as f:
except UnicodeError: data = json.load(f)
except (JSONDecodeError, UnicodeDecodeError):
continue continue
try: try:
game = cast(Game, Json.gobject_from_data(Game, data, len(data))) game = Game(data)
except GLib.Error: except TypeError:
continue continue
cover_path = COVERS_DIR / game.game_id cover_path = _COVERS_DIR / game.game_id
for ext in ".gif", ".tiff": for ext in ".gif", ".tiff":
filename = str(cover_path.with_suffix(ext)) filename = str(cover_path.with_suffix(ext))
try: try:
@@ -66,4 +101,4 @@ def load() -> Generator[Game]:
model = Gio.ListStore.new(Game) model = Gio.ListStore.new(Game)
model.splice(0, 0, tuple(load())) model.splice(0, 0, tuple(_load()))