sources: Remove skip_ids
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"],
|
||||
|
||||
40
cartridges/sources/imported.py
Normal file
40
cartridges/sources/imported.py
Normal 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
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user