🎨 Simplified source location user override
This commit is contained in:
@@ -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/")
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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"""
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user