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-FileCopyrightText: Copyright 2025 Zoey Ahmed
# SPDX-FileCopyrightText: Copyright 2025 Jamie Gravendeel
import gettext
import locale
@@ -12,13 +13,14 @@ import gi
gi.require_versions({
"Gtk": "4.0",
"Adw": "1",
"Json": "1.0",
})
from gi.repository import Gio
from gi.repository import Gio, GLib
from .config import LOCALEDIR, PKGDATADIR
DATA_DIR = Path(GLib.get_user_data_dir(), "cartridges")
_RESOURCES = ("data", "icons", "ui")
signal.signal(signal.SIGINT, signal.SIG_DFL)

View File

@@ -1,22 +1,35 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: Copyright 2025 Zoey Ahmed
# SPDX-FileCopyrightText: Copyright 2025 kramo
# SPDX-FileCopyrightText: Copyright 2025 Jamie Gravendeel
import json
from collections.abc import Generator
from pathlib import Path
from typing import cast
from json import JSONDecodeError
from types import UnionType
from typing import Any
from gi.repository import (
Gdk,
Gio,
GLib,
GObject,
Json, # pyright: ignore[reportAttributeAccessIssue]
)
from gi.repository import Gdk, Gio, GLib, GObject
DATA_DIR = Path(GLib.get_user_data_dir(), "cartridges")
GAMES_DIR = DATA_DIR / "games"
COVERS_DIR = DATA_DIR / "covers"
from cartridges import DATA_DIR
_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):
@@ -29,30 +42,52 @@ class Game(GObject.Object):
game_id = GObject.Property(type=str)
source = GObject.Property(type=str)
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)
developer = GObject.Property(type=str)
removed = 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)
def __init__(self, data: dict[str, Any]):
super().__init__()
def load() -> Generator[Game]:
"""Load the user's games from disk."""
for path in GAMES_DIR.glob("*.json"):
for name, (type_, required) in _PROPERTIES.items():
value = data.get(name)
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:
data = path.read_text("utf-8")
except UnicodeError:
with path.open(encoding="utf-8") as f:
data = json.load(f)
except (JSONDecodeError, UnicodeDecodeError):
continue
try:
game = cast(Game, Json.gobject_from_data(Game, data, len(data)))
except GLib.Error:
game = Game(data)
except TypeError:
continue
cover_path = COVERS_DIR / game.game_id
cover_path = _COVERS_DIR / game.game_id
for ext in ".gif", ".tiff":
filename = str(cover_path.with_suffix(ext))
try:
@@ -66,4 +101,4 @@ def load() -> Generator[Game]:
model = Gio.ListStore.new(Game)
model.splice(0, 0, tuple(load()))
model.splice(0, 0, tuple(_load()))