sources: Remove skip_ids

This commit is contained in:
kramo
2025-12-22 21:29:32 +01:00
parent 84972c06d6
commit d85c6e0530
7 changed files with 77 additions and 66 deletions

View File

@@ -2,15 +2,12 @@
# SPDX-FileCopyrightText: Copyright 2025 Zoey Ahmed
# SPDX-FileCopyrightText: Copyright 2025 kramo
from collections.abc import Generator, Iterable
from gettext import gettext as _
from typing import override
from gi.repository import Adw
from cartridges import collections, games
from cartridges.games import Game
from cartridges.sources import Source, heroic, steam
from .config import APP_ID, PREFIX
from .ui.window import Window
@@ -28,20 +25,9 @@ class Application(Adw.Application):
))
self.set_accels_for_action("app.quit", ("<Control>q",))
saved = tuple(games.load())
new = self.import_games(steam, heroic, skip_ids={g.game_id for g in saved})
games.model.splice(0, 0, (*saved, *new))
games.load()
collections.load()
@staticmethod
def import_games(*sources: Source, skip_ids: Iterable[str]) -> Generator[Game]:
"""Import games from `sources`, skipping ones in `skip_ids`."""
for source in sources:
try:
yield from source.get_games(skip_ids=skip_ids)
except OSError:
continue
@override
def do_startup(self):
Adw.Application.do_startup(self)

View File

@@ -7,17 +7,16 @@ import itertools
import json
import os
import subprocess
from collections.abc import Callable, Generator, Iterable
from collections.abc import Callable, Iterable
from gettext import gettext as _
from json import JSONDecodeError
from pathlib import Path
from shlex import quote
from types import UnionType
from typing import TYPE_CHECKING, Any, NamedTuple, Self, cast
from gi.repository import Gdk, Gio, GLib, GObject
from gi.repository import Gdk, Gio, GObject
from cartridges import DATA_DIR
from . import DATA_DIR
if TYPE_CHECKING:
from .application import Application
@@ -45,8 +44,8 @@ PROPERTIES: tuple[_GameProp, ...] = (
_GameProp("version", float),
)
_GAMES_DIR = DATA_DIR / "games"
_COVERS_DIR = DATA_DIR / "covers"
GAMES_DIR = DATA_DIR / "games"
COVERS_DIR = DATA_DIR / "covers"
_SPEC_VERSION = 2.0
_MANUALLY_ADDED_ID = "imported"
@@ -150,8 +149,8 @@ class Game(Gio.SimpleActionGroup):
"""Save the game's properties to disk."""
properties = {prop.name: getattr(self, prop.name) for prop in PROPERTIES}
_GAMES_DIR.mkdir(parents=True, exist_ok=True)
path = (_GAMES_DIR / self.game_id).with_suffix(".json")
GAMES_DIR.mkdir(parents=True, exist_ok=True)
path = (GAMES_DIR / self.game_id).with_suffix(".json")
with path.open(encoding="utf-8") as f:
json.dump(properties, f, indent=4)
@@ -182,6 +181,13 @@ class Game(Gio.SimpleActionGroup):
window.send_toast(title, undo=undo)
def load():
"""Populate `games.model` with all games from all sources."""
from . import sources
model.splice(0, 0, tuple(sources.get_games()))
def _increment_manually_added_id() -> int:
numbers = {
game.game_id.split("_")[1]
@@ -196,31 +202,4 @@ def _increment_manually_added_id() -> int:
raise ValueError
def load() -> Generator[Game]:
"""Load previously saved games from disk."""
for path in _GAMES_DIR.glob("*.json"):
try:
with path.open(encoding="utf-8") as f:
data = json.load(f)
except (JSONDecodeError, UnicodeDecodeError):
continue
try:
game = Game.from_data(data)
except TypeError:
continue
cover_path = _COVERS_DIR / game.game_id
for ext in ".gif", ".tiff":
filename = str(cover_path.with_suffix(ext))
try:
game.cover = Gdk.Texture.new_from_filename(filename)
except GLib.Error:
continue
else:
break
yield game
model = Gio.ListStore.new(Game)

View File

@@ -3,7 +3,7 @@
import os
import sys
from collections.abc import Generator, Iterable
from collections.abc import Generator
from pathlib import Path
from typing import Final, Protocol
@@ -44,6 +44,17 @@ class Source(Protocol):
NAME: Final[str]
@staticmethod
def get_games(*, skip_ids: Iterable[str]) -> Generator[Game]:
"""Installed games, except those in `skip_ids`."""
def get_games() -> Generator[Game]:
"""Installed games."""
...
def get_games() -> Generator[Game]:
"""Installed games from all sources."""
from . import heroic, imported, steam
for source in heroic, imported, steam:
try:
yield from source.get_games()
except OSError:
continue

View File

@@ -111,11 +111,11 @@ class _NileSource(_StoreSource):
yield entry["id"]
def get_games(*, skip_ids: Iterable[str]) -> Generator[Game]:
def get_games() -> Generator[Game]:
"""Installed Heroic games."""
added = int(time.time())
for source in _LegendarySource, _GOGSource, _NileSource, _SideloadSource:
yield from _games_from(source, added, skip_ids)
yield from _games_from(source, added)
def _config_dir() -> Path:
@@ -139,9 +139,7 @@ def _hidden_app_names() -> Generator[str]:
yield game["appName"]
def _games_from(
source: type[_Source], added: int, skip_ids: Iterable[str]
) -> Generator[Game]:
def _games_from(source: type[_Source], added: int) -> Generator[Game]:
try:
with (_config_dir() / source.library_path()).open() as fp:
library = json.load(fp)
@@ -164,10 +162,6 @@ def _games_from(
if (installed is not None) and (app_name not in installed):
continue
game_id = f"{source_id}_{app_name}"
if game_id in skip_ids:
continue
cover_uri = f"{entry.get('art_square', '')}{source.COVER_URI_PARAMS}"
cover_path = images_cache / sha256(cover_uri.encode()).hexdigest()
@@ -179,7 +173,7 @@ def _games_from(
yield Game(
added=added,
executable=f"{OPEN} heroic://launch/{entry['runner']}/{app_name}",
game_id=game_id,
game_id=f"{source_id}_{app_name}",
source=source_id,
hidden=app_name in hidden,
name=entry["title"],

View File

@@ -0,0 +1,40 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: Copyright 2025 kramo
import json
from collections.abc import Generator
from gettext import gettext as _
from json import JSONDecodeError
from gi.repository import Gdk, GLib
from cartridges.games import COVERS_DIR, GAMES_DIR, Game
ID, NAME = "imported", _("Added")
def get_games() -> Generator[Game]:
"""Manually added games."""
for path in GAMES_DIR.glob("imported_*.json"):
try:
with path.open(encoding="utf-8") as f:
data = json.load(f)
except (JSONDecodeError, UnicodeDecodeError):
continue
try:
game = Game.from_data(data)
except TypeError:
continue
cover_path = COVERS_DIR / game.game_id
for ext in ".gif", ".tiff":
filename = str(cover_path.with_suffix(ext))
try:
game.cover = Gdk.Texture.new_from_filename(filename)
except GLib.Error:
continue
else:
break
yield game

View File

@@ -8,7 +8,7 @@ import re
import struct
import time
from collections import defaultdict
from collections.abc import Generator, Iterable, Sequence
from collections.abc import Generator, Sequence
from contextlib import suppress
from gettext import gettext as _
from os import SEEK_CUR
@@ -107,7 +107,7 @@ class _AppInfo(NamedTuple):
return cls(common.get("type"), developer, capsule)
def get_games(*, skip_ids: Iterable[str]) -> Generator[Game]:
def get_games() -> Generator[Game]:
"""Installed Steam games."""
added = int(time.time())
@@ -115,7 +115,7 @@ def get_games(*, skip_ids: Iterable[str]) -> Generator[Game]:
with (_data_dir() / "appcache" / "appinfo.vdf").open("rb") as fp:
appinfo = defaultdict(_AppInfo, _parse_appinfo_vdf(fp))
appids = {i.rsplit("_", 1)[-1] for i in skip_ids if i.startswith(f"{ID}_")}
appids = set()
for manifest in _manifests():
try:
name, appid, stateflags, lastplayed = _App.from_manifest(manifest)

View File

@@ -4,6 +4,7 @@ cartridges/gamepads.py
cartridges/games.py
cartridges/sources/__init__.py
cartridges/sources/heroic.py
cartridges/sources/imported.py
cartridges/sources/steam.py
cartridges/ui/collections.py
cartridges/ui/cover.blp