games: Use Python's JSON module
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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()))
|
||||
|
||||
Reference in New Issue
Block a user