🎨 Simplified source location user override

This commit is contained in:
GeoffreyCoulaud
2023-06-05 13:11:05 +02:00
parent 1dcfe38253
commit 1e3e6484e4
6 changed files with 46 additions and 62 deletions

View File

@@ -1,17 +1,13 @@
from pathlib import Path from pathlib import Path
from time import time from time import time
from typing import Generator, Optional from typing import Optional
import yaml import yaml
from src import shared from src import shared
from src.game import Game from src.game import Game
from src.importer.sources.source import LinuxSource, Source, SourceIterator from src.importer.sources.source import LinuxSource, Source, SourceIterator
from src.utils.decorators import ( from src.utils.decorators import replaced_by_env_path, replaced_by_path
replaced_by_env_path,
replaced_by_path,
replaced_by_schema_key,
)
from src.utils.save_cover import resize_cover, save_cover from src.utils.save_cover import resize_cover, save_cover
@@ -55,6 +51,7 @@ class BottlesSource(Source):
"""Generic Bottles source""" """Generic Bottles source"""
name = "Bottles" name = "Bottles"
location_key = "bottles-location"
def __iter__(self) -> SourceIterator: def __iter__(self) -> SourceIterator:
return BottlesSourceIterator(self) return BottlesSourceIterator(self)
@@ -65,7 +62,7 @@ class BottlesLinuxSource(BottlesSource, LinuxSource):
executable_format = 'xdg-open bottles:run/"{bottle_name}"/"{game_name}"' executable_format = 'xdg-open bottles:run/"{bottle_name}"/"{game_name}"'
@property @property
@replaced_by_schema_key("bottles-location") @Source.replaced_by_schema_key()
@replaced_by_path("~/.var/app/com.usebottles.bottles/data/bottles/") @replaced_by_path("~/.var/app/com.usebottles.bottles/data/bottles/")
@replaced_by_env_path("XDG_DATA_HOME", "bottles/") @replaced_by_env_path("XDG_DATA_HOME", "bottles/")
@replaced_by_path("~/.local/share/bottles/") @replaced_by_path("~/.local/share/bottles/")

View File

@@ -4,7 +4,7 @@ from hashlib import sha256
from json import JSONDecodeError from json import JSONDecodeError
from pathlib import Path from pathlib import Path
from time import time from time import time
from typing import Generator, Optional, TypedDict from typing import Optional, TypedDict
from src import shared from src import shared
from src.game import Game from src.game import Game
@@ -14,11 +14,7 @@ from src.importer.sources.source import (
SourceIterator, SourceIterator,
WindowsSource, WindowsSource,
) )
from src.utils.decorators import ( from src.utils.decorators import replaced_by_env_path, replaced_by_path
replaced_by_env_path,
replaced_by_path,
replaced_by_schema_key,
)
from src.utils.save_cover import resize_cover, save_cover from src.utils.save_cover import resize_cover, save_cover
@@ -118,6 +114,7 @@ class HeroicSource(Source):
"""Generic heroic games launcher source""" """Generic heroic games launcher source"""
name = "Heroic" name = "Heroic"
location_key = "heroic-location"
@property @property
def game_id_format(self) -> str: def game_id_format(self) -> str:
@@ -133,7 +130,7 @@ class HeroicLinuxSource(HeroicSource, LinuxSource):
executable_format = "xdg-open heroic://launch/{app_name}" executable_format = "xdg-open heroic://launch/{app_name}"
@property @property
@replaced_by_schema_key("heroic-location") @Source.replaced_by_schema_key()
@replaced_by_path("~/.var/app/com.heroicgameslauncher.hgl/config/heroic/") @replaced_by_path("~/.var/app/com.heroicgameslauncher.hgl/config/heroic/")
@replaced_by_env_path("XDG_CONFIG_HOME", "heroic/") @replaced_by_env_path("XDG_CONFIG_HOME", "heroic/")
@replaced_by_path("~/.config/heroic/") @replaced_by_path("~/.config/heroic/")
@@ -146,7 +143,7 @@ class HeroicWindowsSource(HeroicSource, WindowsSource):
executable_format = "start heroic://launch/{app_name}" executable_format = "start heroic://launch/{app_name}"
@property @property
@replaced_by_schema_key("heroic-location") @Source.replaced_by_schema_key()
@replaced_by_env_path("appdata", "heroic/") @replaced_by_env_path("appdata", "heroic/")
def location(self) -> Path: def location(self) -> Path:
raise FileNotFoundError() raise FileNotFoundError()

View File

@@ -1,11 +1,11 @@
from sqlite3 import connect from sqlite3 import connect
from time import time from time import time
from typing import Optional, Generator from typing import Optional
from src import shared from src import shared
from src.game import Game from src.game import Game
from src.importer.sources.source import LinuxSource, Source, SourceIterator 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 from src.utils.save_cover import resize_cover, save_cover
@@ -61,6 +61,7 @@ class LutrisSource(Source):
"""Generic lutris source""" """Generic lutris source"""
name = "Lutris" name = "Lutris"
location_key = "lutris-location"
@property @property
def game_id_format(self): def game_id_format(self):
@@ -75,7 +76,7 @@ class LutrisLinuxSource(LutrisSource, LinuxSource):
executable_format = "xdg-open lutris:rungameid/{game_id}" executable_format = "xdg-open lutris:rungameid/{game_id}"
@property @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("~/.var/app/net.lutris.Lutris/data/lutris/")
@replaced_by_path("~/.local/share/lutris/") @replaced_by_path("~/.local/share/lutris/")
def location(self): def location(self):

View File

@@ -1,10 +1,13 @@
import sys import sys
from abc import abstractmethod from abc import abstractmethod
from collections.abc import Iterable, Iterator from collections.abc import Iterable, Iterator
from functools import wraps
from pathlib import Path 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.game import Game
from src.utils.decorators import replaced_by_path
class SourceIterator(Iterator): class SourceIterator(Iterator):
@@ -40,11 +43,13 @@ class Source(Iterable):
name: str name: str
variant: str variant: str
location_key: str
available_on: set[str] available_on: set[str]
def __init__(self) -> None: def __init__(self) -> None:
super().__init__() super().__init__()
self.available_on = set() self.available_on = set()
self.update_location_schema_key()
@property @property
def full_name(self) -> str: def full_name(self) -> str:
@@ -76,6 +81,28 @@ class Source(Iterable):
return False return False
return sys.platform in self.available_on 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 @property
@abstractmethod @abstractmethod
def location(self) -> Path: def location(self) -> Path:

View File

@@ -1,7 +1,7 @@
import re import re
from pathlib import Path from pathlib import Path
from time import time from time import time
from typing import Iterable, Optional, Generator from typing import Iterable, Optional
from src import shared from src import shared
from src.game import Game from src.game import Game
@@ -11,11 +11,7 @@ from src.importer.sources.source import (
SourceIterator, SourceIterator,
WindowsSource, WindowsSource,
) )
from src.utils.decorators import ( from src.utils.decorators import replaced_by_env_path, replaced_by_path
replaced_by_env_path,
replaced_by_path,
replaced_by_schema_key,
)
from src.utils.save_cover import resize_cover, save_cover from src.utils.save_cover import resize_cover, save_cover
from src.utils.steam import SteamHelper, SteamInvalidManifestError from src.utils.steam import SteamHelper, SteamInvalidManifestError
@@ -98,6 +94,7 @@ class SteamSourceIterator(SourceIterator):
class SteamSource(Source): class SteamSource(Source):
name = "Steam" name = "Steam"
location_key = "steam-location"
def __iter__(self): def __iter__(self):
return SteamSourceIterator(source=self) return SteamSourceIterator(source=self)
@@ -108,7 +105,7 @@ class SteamLinuxSource(SteamSource, LinuxSource):
executable_format = "xdg-open steam://rungameid/{game_id}" executable_format = "xdg-open steam://rungameid/{game_id}"
@property @property
@replaced_by_schema_key("steam-location") @Source.replaced_by_schema_key()
@replaced_by_path("~/.var/app/com.valvesoftware.Steam/data/Steam/") @replaced_by_path("~/.var/app/com.valvesoftware.Steam/data/Steam/")
@replaced_by_env_path("XDG_DATA_HOME", "Steam/") @replaced_by_env_path("XDG_DATA_HOME", "Steam/")
@replaced_by_path("~/.steam/") @replaced_by_path("~/.steam/")
@@ -122,7 +119,7 @@ class SteamWindowsSource(SteamSource, WindowsSource):
executable_format = "start steam://rungameid/{game_id}" executable_format = "start steam://rungameid/{game_id}"
@property @property
@replaced_by_schema_key("steam-location") @Source.replaced_by_schema_key()
@replaced_by_env_path("programfiles(x86)", "Steam") @replaced_by_env_path("programfiles(x86)", "Steam")
def location(self): def location(self):
raise FileNotFoundError() raise FileNotFoundError()

View File

@@ -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 pathlib import Path
from os import PathLike, environ from os import PathLike, environ
from functools import wraps from functools import wraps
from src import shared
def replaced_by_path(override: PathLike): # Decorator builder def replaced_by_path(override: PathLike): # Decorator builder
"""Replace the method's returned path with the override """Replace the method's returned path with the override
@@ -37,24 +20,6 @@ def replaced_by_path(override: PathLike): # Decorator builder
return decorator 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): 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""" """Replace the method's returned path with a path whose root is the env variable"""