From 1e3e6484e40f8c56723269297fdd78e197730678 Mon Sep 17 00:00:00 2001 From: GeoffreyCoulaud Date: Mon, 5 Jun 2023 13:11:05 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20Simplified=20source=20location?= =?UTF-8?q?=20user=20override?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/importer/sources/bottles_source.py | 11 +++----- src/importer/sources/heroic_source.py | 13 ++++------ src/importer/sources/lutris_source.py | 7 +++--- src/importer/sources/source.py | 29 ++++++++++++++++++++- src/importer/sources/steam_source.py | 13 ++++------ src/utils/decorators.py | 35 -------------------------- 6 files changed, 46 insertions(+), 62 deletions(-) diff --git a/src/importer/sources/bottles_source.py b/src/importer/sources/bottles_source.py index 8e84869..b2c0a2d 100644 --- a/src/importer/sources/bottles_source.py +++ b/src/importer/sources/bottles_source.py @@ -1,17 +1,13 @@ from pathlib import Path from time import time -from typing import Generator, Optional +from typing import Optional import yaml from src import shared from src.game import Game from src.importer.sources.source import LinuxSource, Source, SourceIterator -from src.utils.decorators import ( - replaced_by_env_path, - replaced_by_path, - replaced_by_schema_key, -) +from src.utils.decorators import replaced_by_env_path, replaced_by_path from src.utils.save_cover import resize_cover, save_cover @@ -55,6 +51,7 @@ class BottlesSource(Source): """Generic Bottles source""" name = "Bottles" + location_key = "bottles-location" def __iter__(self) -> SourceIterator: return BottlesSourceIterator(self) @@ -65,7 +62,7 @@ class BottlesLinuxSource(BottlesSource, LinuxSource): executable_format = 'xdg-open bottles:run/"{bottle_name}"/"{game_name}"' @property - @replaced_by_schema_key("bottles-location") + @Source.replaced_by_schema_key() @replaced_by_path("~/.var/app/com.usebottles.bottles/data/bottles/") @replaced_by_env_path("XDG_DATA_HOME", "bottles/") @replaced_by_path("~/.local/share/bottles/") diff --git a/src/importer/sources/heroic_source.py b/src/importer/sources/heroic_source.py index 1e2259d..998bf65 100644 --- a/src/importer/sources/heroic_source.py +++ b/src/importer/sources/heroic_source.py @@ -4,7 +4,7 @@ from hashlib import sha256 from json import JSONDecodeError from pathlib import Path from time import time -from typing import Generator, Optional, TypedDict +from typing import Optional, TypedDict from src import shared from src.game import Game @@ -14,11 +14,7 @@ from src.importer.sources.source import ( SourceIterator, WindowsSource, ) -from src.utils.decorators import ( - replaced_by_env_path, - replaced_by_path, - replaced_by_schema_key, -) +from src.utils.decorators import replaced_by_env_path, replaced_by_path from src.utils.save_cover import resize_cover, save_cover @@ -118,6 +114,7 @@ class HeroicSource(Source): """Generic heroic games launcher source""" name = "Heroic" + location_key = "heroic-location" @property def game_id_format(self) -> str: @@ -133,7 +130,7 @@ class HeroicLinuxSource(HeroicSource, LinuxSource): executable_format = "xdg-open heroic://launch/{app_name}" @property - @replaced_by_schema_key("heroic-location") + @Source.replaced_by_schema_key() @replaced_by_path("~/.var/app/com.heroicgameslauncher.hgl/config/heroic/") @replaced_by_env_path("XDG_CONFIG_HOME", "heroic/") @replaced_by_path("~/.config/heroic/") @@ -146,7 +143,7 @@ class HeroicWindowsSource(HeroicSource, WindowsSource): executable_format = "start heroic://launch/{app_name}" @property - @replaced_by_schema_key("heroic-location") + @Source.replaced_by_schema_key() @replaced_by_env_path("appdata", "heroic/") def location(self) -> Path: raise FileNotFoundError() diff --git a/src/importer/sources/lutris_source.py b/src/importer/sources/lutris_source.py index 7bb662b..1633445 100644 --- a/src/importer/sources/lutris_source.py +++ b/src/importer/sources/lutris_source.py @@ -1,11 +1,11 @@ from sqlite3 import connect from time import time -from typing import Optional, Generator +from typing import Optional from src import shared from src.game import Game from src.importer.sources.source import LinuxSource, Source, SourceIterator -from src.utils.decorators import replaced_by_path, replaced_by_schema_key +from src.utils.decorators import replaced_by_path from src.utils.save_cover import resize_cover, save_cover @@ -61,6 +61,7 @@ class LutrisSource(Source): """Generic lutris source""" name = "Lutris" + location_key = "lutris-location" @property def game_id_format(self): @@ -75,7 +76,7 @@ class LutrisLinuxSource(LutrisSource, LinuxSource): executable_format = "xdg-open lutris:rungameid/{game_id}" @property - @replaced_by_schema_key("lutris-location") + @Source.replaced_by_schema_key() @replaced_by_path("~/.var/app/net.lutris.Lutris/data/lutris/") @replaced_by_path("~/.local/share/lutris/") def location(self): diff --git a/src/importer/sources/source.py b/src/importer/sources/source.py index f0da552..cd9857d 100644 --- a/src/importer/sources/source.py +++ b/src/importer/sources/source.py @@ -1,10 +1,13 @@ import sys from abc import abstractmethod from collections.abc import Iterable, Iterator +from functools import wraps from pathlib import Path -from typing import Optional, Generator +from typing import Generator, Optional +from src import shared from src.game import Game +from src.utils.decorators import replaced_by_path class SourceIterator(Iterator): @@ -40,11 +43,13 @@ class Source(Iterable): name: str variant: str + location_key: str available_on: set[str] def __init__(self) -> None: super().__init__() self.available_on = set() + self.update_location_schema_key() @property def full_name(self) -> str: @@ -76,6 +81,28 @@ class Source(Iterable): return False return sys.platform in self.available_on + def update_location_schema_key(self): + """Update the schema value for this source's location if possible""" + try: + location = self.location + except FileNotFoundError: + return + shared.schema.set_string(self.location_key, location) + + @classmethod + def replaced_by_schema_key(cls): # Decorator builder + """Replace the returned path with schema's path if valid""" + + def decorator(original_function): # Built decorator (closure) + @wraps(original_function) + def wrapper(*args, **kwargs): # func's override + override = shared.schema.get_string(cls.location_key) + return replaced_by_path(override)(original_function)(*args, **kwargs) + + return wrapper + + return decorator + @property @abstractmethod def location(self) -> Path: diff --git a/src/importer/sources/steam_source.py b/src/importer/sources/steam_source.py index 5e59252..cff8c87 100644 --- a/src/importer/sources/steam_source.py +++ b/src/importer/sources/steam_source.py @@ -1,7 +1,7 @@ import re from pathlib import Path from time import time -from typing import Iterable, Optional, Generator +from typing import Iterable, Optional from src import shared from src.game import Game @@ -11,11 +11,7 @@ from src.importer.sources.source import ( SourceIterator, WindowsSource, ) -from src.utils.decorators import ( - replaced_by_env_path, - replaced_by_path, - replaced_by_schema_key, -) +from src.utils.decorators import replaced_by_env_path, replaced_by_path from src.utils.save_cover import resize_cover, save_cover from src.utils.steam import SteamHelper, SteamInvalidManifestError @@ -98,6 +94,7 @@ class SteamSourceIterator(SourceIterator): class SteamSource(Source): name = "Steam" + location_key = "steam-location" def __iter__(self): return SteamSourceIterator(source=self) @@ -108,7 +105,7 @@ class SteamLinuxSource(SteamSource, LinuxSource): executable_format = "xdg-open steam://rungameid/{game_id}" @property - @replaced_by_schema_key("steam-location") + @Source.replaced_by_schema_key() @replaced_by_path("~/.var/app/com.valvesoftware.Steam/data/Steam/") @replaced_by_env_path("XDG_DATA_HOME", "Steam/") @replaced_by_path("~/.steam/") @@ -122,7 +119,7 @@ class SteamWindowsSource(SteamSource, WindowsSource): executable_format = "start steam://rungameid/{game_id}" @property - @replaced_by_schema_key("steam-location") + @Source.replaced_by_schema_key() @replaced_by_env_path("programfiles(x86)", "Steam") def location(self): raise FileNotFoundError() diff --git a/src/utils/decorators.py b/src/utils/decorators.py index cce9a09..f836945 100644 --- a/src/utils/decorators.py +++ b/src/utils/decorators.py @@ -1,24 +1,7 @@ -""" -A decorator takes a callable A and returns a callable B that will override the name of A. -A decorator with arguments returns a closure decorator having access to the arguments. - -Example usage for the location decorators: - -class MyClass(): - @cached_property - @replaced_by_schema_key(key="source-location") - @replaced_by_path(override="/somewhere/that/doesnt/exist") - @replaced_by_path(override="/somewhere/that/exists") - def location(self): - return None -""" - from pathlib import Path from os import PathLike, environ from functools import wraps -from src import shared - def replaced_by_path(override: PathLike): # Decorator builder """Replace the method's returned path with the override @@ -37,24 +20,6 @@ def replaced_by_path(override: PathLike): # Decorator builder return decorator -def replaced_by_schema_key(key: str): # Decorator builder - """Replace the method's returned path with the path pointed by the key - if it exists on disk""" - - def decorator(original_function): # Built decorator (closure) - @wraps(original_function) - def wrapper(*args, **kwargs): # func's override - try: - override = shared.schema.get_string(key) - except Exception: # pylint: disable=broad-exception-caught - return original_function(*args, **kwargs) - return replaced_by_path(override)(original_function)(*args, **kwargs) - - return wrapper - - return decorator - - def replaced_by_env_path(env_var_name: str, suffix: PathLike | None = None): """Replace the method's returned path with a path whose root is the env variable"""