🎨 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 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/")

View File

@@ -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()

View File

@@ -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):

View File

@@ -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:

View File

@@ -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()

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 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"""