Merge pull request #158 from kra-mo/locations-improvements
Locations improvements
This commit is contained in:
@@ -20,12 +20,13 @@
|
||||
|
||||
from pathlib import Path
|
||||
from time import time
|
||||
from typing import NamedTuple
|
||||
|
||||
import yaml
|
||||
|
||||
from src import shared
|
||||
from src.game import Game
|
||||
from src.importer.sources.location import Location
|
||||
from src.importer.sources.location import Location, LocationSubPath
|
||||
from src.importer.sources.source import SourceIterable, URLExecutableSource
|
||||
|
||||
|
||||
@@ -35,7 +36,7 @@ class BottlesSourceIterable(SourceIterable):
|
||||
def __iter__(self):
|
||||
"""Generator method producing games"""
|
||||
|
||||
data = self.source.data_location["library.yml"].read_text("utf-8")
|
||||
data = self.source.locations.data["library.yml"].read_text("utf-8")
|
||||
library: dict = yaml.safe_load(data)
|
||||
added_time = int(time())
|
||||
|
||||
@@ -58,11 +59,11 @@ class BottlesSourceIterable(SourceIterable):
|
||||
# as Cartridges can't access directories picked via Bottles' file picker portal
|
||||
bottles_location = Path(
|
||||
yaml.safe_load(
|
||||
self.source.data_location["data.yml"].read_text("utf-8")
|
||||
self.source.locations.data["data.yml"].read_text("utf-8")
|
||||
)["custom_bottles_path"]
|
||||
)
|
||||
except (FileNotFoundError, KeyError):
|
||||
bottles_location = self.source.data_location.root / "bottles"
|
||||
bottles_location = self.source.locations.data.root / "bottles"
|
||||
|
||||
bottle_path = entry["bottle"]["path"]
|
||||
|
||||
@@ -76,6 +77,10 @@ class BottlesSourceIterable(SourceIterable):
|
||||
yield (game, additional_data)
|
||||
|
||||
|
||||
class BottlesLocations(NamedTuple):
|
||||
data: Location
|
||||
|
||||
|
||||
class BottlesSource(URLExecutableSource):
|
||||
"""Generic Bottles source"""
|
||||
|
||||
@@ -84,7 +89,8 @@ class BottlesSource(URLExecutableSource):
|
||||
url_format = 'bottles:run/"{bottle_name}"/"{game_name}"'
|
||||
available_on = {"linux"}
|
||||
|
||||
data_location = Location(
|
||||
locations = BottlesLocations(
|
||||
Location(
|
||||
schema_key="bottles-location",
|
||||
candidates=(
|
||||
shared.flatpak_dir / "com.usebottles.bottles" / "data" / "bottles",
|
||||
@@ -92,7 +98,9 @@ class BottlesSource(URLExecutableSource):
|
||||
shared.home / ".local" / "share" / "bottles",
|
||||
),
|
||||
paths={
|
||||
"library.yml": (False, "library.yml"),
|
||||
"data.yml": (False, "data.yml"),
|
||||
"library.yml": LocationSubPath("library.yml"),
|
||||
"data.yml": LocationSubPath("data.yml"),
|
||||
},
|
||||
invalid_subtitle=Location.DATA_INVALID_SUBTITLE,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -19,12 +19,13 @@
|
||||
|
||||
from pathlib import Path
|
||||
from time import time
|
||||
from typing import NamedTuple
|
||||
|
||||
from gi.repository import GLib, Gtk
|
||||
|
||||
from src import shared
|
||||
from src.game import Game
|
||||
from src.importer.sources.location import Location
|
||||
from src.importer.sources.location import Location, LocationSubPath
|
||||
from src.importer.sources.source import Source, SourceIterable
|
||||
|
||||
|
||||
@@ -37,7 +38,7 @@ class FlatpakSourceIterable(SourceIterable):
|
||||
added_time = int(time())
|
||||
|
||||
icon_theme = Gtk.IconTheme.new()
|
||||
icon_theme.add_search_path(str(self.source.data_location["icons"]))
|
||||
icon_theme.add_search_path(str(self.source.locations.data["icons"]))
|
||||
|
||||
blacklist = (
|
||||
{"hu.kramo.Cartridges", "hu.kramo.Cartridges.Devel"}
|
||||
@@ -53,7 +54,7 @@ class FlatpakSourceIterable(SourceIterable):
|
||||
}
|
||||
)
|
||||
|
||||
for entry in (self.source.data_location["applications"]).iterdir():
|
||||
for entry in (self.source.locations.data["applications"]).iterdir():
|
||||
if entry.suffix != ".desktop":
|
||||
continue
|
||||
|
||||
@@ -111,6 +112,10 @@ class FlatpakSourceIterable(SourceIterable):
|
||||
yield (game, additional_data)
|
||||
|
||||
|
||||
class FlatpakLocations(NamedTuple):
|
||||
data: Location
|
||||
|
||||
|
||||
class FlatpakSource(Source):
|
||||
"""Generic Flatpak source"""
|
||||
|
||||
@@ -119,14 +124,17 @@ class FlatpakSource(Source):
|
||||
executable_format = "flatpak run {flatpak_id}"
|
||||
available_on = {"linux"}
|
||||
|
||||
data_location = Location(
|
||||
locations = FlatpakLocations(
|
||||
Location(
|
||||
schema_key="flatpak-location",
|
||||
candidates=(
|
||||
"/var/lib/flatpak/",
|
||||
shared.data_dir / "flatpak",
|
||||
),
|
||||
paths={
|
||||
"applications": (True, "exports/share/applications"),
|
||||
"icons": (True, "exports/share/icons"),
|
||||
"applications": LocationSubPath("exports/share/applications", True),
|
||||
"icons": LocationSubPath("exports/share/icons", True),
|
||||
},
|
||||
invalid_subtitle=Location.DATA_INVALID_SUBTITLE,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -25,12 +25,12 @@ from hashlib import sha256
|
||||
from json import JSONDecodeError
|
||||
from pathlib import Path
|
||||
from time import time
|
||||
from typing import Iterable, Optional, TypedDict
|
||||
from typing import Iterable, NamedTuple, Optional, TypedDict
|
||||
from functools import cached_property
|
||||
|
||||
from src import shared
|
||||
from src.game import Game
|
||||
from src.importer.sources.location import Location
|
||||
from src.importer.sources.location import Location, LocationSubPath
|
||||
from src.importer.sources.source import (
|
||||
SourceIterable,
|
||||
SourceIterationResult,
|
||||
@@ -87,7 +87,7 @@ class SubSourceIterable(Iterable):
|
||||
|
||||
@cached_property
|
||||
def library_path(self) -> Path:
|
||||
path = self.source.config_location.root / self.relative_library_path
|
||||
path = self.source.locations.config.root / self.relative_library_path
|
||||
logging.debug("Using Heroic %s library.json path %s", self.name, path)
|
||||
return path
|
||||
|
||||
@@ -116,7 +116,7 @@ class SubSourceIterable(Iterable):
|
||||
# Filenames are derived from the URL that heroic used to get the file
|
||||
uri: str = entry["art_square"] + self.image_uri_params
|
||||
digest = sha256(uri.encode()).hexdigest()
|
||||
image_path = self.source.config_location.root / "images-cache" / digest
|
||||
image_path = self.source.locations.config.root / "images-cache" / digest
|
||||
additional_data = {"local_image_path": image_path}
|
||||
|
||||
return (game, additional_data)
|
||||
@@ -159,7 +159,7 @@ class StoreSubSourceIterable(SubSourceIterable):
|
||||
|
||||
@cached_property
|
||||
def installed_path(self) -> Path:
|
||||
path = self.source.config_location.root / self.relative_installed_path
|
||||
path = self.source.locations.config.root / self.relative_installed_path
|
||||
logging.debug("Using Heroic %s installed.json path %s", self.name, path)
|
||||
return path
|
||||
|
||||
@@ -226,7 +226,7 @@ class LegendaryIterable(StoreSubSourceIterable):
|
||||
and remove this property override.
|
||||
"""
|
||||
|
||||
heroic_config_path = self.source.config_location.root
|
||||
heroic_config_path = self.source.locations.config.root
|
||||
# Heroic >= 2.9
|
||||
if (path := heroic_config_path / "legendaryConfig").is_dir():
|
||||
logging.debug("Using Heroic >= 2.9 legendary file")
|
||||
@@ -308,7 +308,7 @@ class HeroicSourceIterable(SourceIterable):
|
||||
"""
|
||||
|
||||
try:
|
||||
store = path_json_load(self.source.config_location["store_config.json"])
|
||||
store = path_json_load(self.source.locations.config["store_config.json"])
|
||||
self.hidden_app_names = {
|
||||
app_name
|
||||
for game in store["games"]["hidden"]
|
||||
@@ -349,6 +349,10 @@ class HeroicSourceIterable(SourceIterable):
|
||||
continue
|
||||
|
||||
|
||||
class HeroicLocations(NamedTuple):
|
||||
config: Location
|
||||
|
||||
|
||||
class HeroicSource(URLExecutableSource):
|
||||
"""Generic Heroic Games Launcher source"""
|
||||
|
||||
@@ -357,18 +361,24 @@ class HeroicSource(URLExecutableSource):
|
||||
url_format = "heroic://launch/{app_name}"
|
||||
available_on = {"linux", "win32"}
|
||||
|
||||
config_location = Location(
|
||||
locations = HeroicLocations(
|
||||
Location(
|
||||
schema_key="heroic-location",
|
||||
candidates=(
|
||||
shared.config_dir / "heroic",
|
||||
shared.home / ".config" / "heroic",
|
||||
shared.flatpak_dir / "com.heroicgameslauncher.hgl" / "config" / "heroic",
|
||||
shared.flatpak_dir
|
||||
/ "com.heroicgameslauncher.hgl"
|
||||
/ "config"
|
||||
/ "heroic",
|
||||
shared.appdata_dir / "heroic",
|
||||
),
|
||||
paths={
|
||||
"config.json": (False, "config.json"),
|
||||
"store_config.json": (False, Path("store") / "config.json"),
|
||||
"config.json": LocationSubPath("config.json"),
|
||||
"store_config.json": LocationSubPath("store/config.json"),
|
||||
},
|
||||
invalid_subtitle=Location.CONFIG_INVALID_SUBTITLE,
|
||||
)
|
||||
)
|
||||
|
||||
@property
|
||||
|
||||
@@ -21,10 +21,11 @@
|
||||
from shutil import rmtree
|
||||
from sqlite3 import connect
|
||||
from time import time
|
||||
from typing import NamedTuple
|
||||
|
||||
from src import shared
|
||||
from src.game import Game
|
||||
from src.importer.sources.location import Location
|
||||
from src.importer.sources.location import Location, LocationSubPath
|
||||
from src.importer.sources.source import SourceIterable, URLExecutableSource
|
||||
from src.utils.sqlite import copy_db
|
||||
|
||||
@@ -51,7 +52,7 @@ class ItchSourceIterable(SourceIterable):
|
||||
caves.game_id = games.id
|
||||
;
|
||||
"""
|
||||
db_path = copy_db(self.source.config_location["butler.db"])
|
||||
db_path = copy_db(self.source.locations.config["butler.db"])
|
||||
connection = connect(db_path)
|
||||
cursor = connection.execute(db_request)
|
||||
|
||||
@@ -74,13 +75,18 @@ class ItchSourceIterable(SourceIterable):
|
||||
rmtree(str(db_path.parent))
|
||||
|
||||
|
||||
class ItchLocations(NamedTuple):
|
||||
config: Location
|
||||
|
||||
|
||||
class ItchSource(URLExecutableSource):
|
||||
name = _("itch")
|
||||
iterable_class = ItchSourceIterable
|
||||
url_format = "itch://caves/{cave_id}/launch"
|
||||
available_on = {"linux", "win32"}
|
||||
|
||||
config_location = Location(
|
||||
locations = ItchLocations(
|
||||
Location(
|
||||
schema_key="itch-location",
|
||||
candidates=(
|
||||
shared.flatpak_dir / "io.itch.itch" / "config" / "itch",
|
||||
@@ -88,5 +94,9 @@ class ItchSource(URLExecutableSource):
|
||||
shared.home / ".config" / "itch",
|
||||
shared.appdata_dir / "itch",
|
||||
),
|
||||
paths={"butler.db": (False, "db/butler.db")},
|
||||
paths={
|
||||
"butler.db": LocationSubPath("db/butler.db"),
|
||||
},
|
||||
invalid_subtitle=Location.CONFIG_INVALID_SUBTITLE,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -21,10 +21,11 @@ import json
|
||||
import logging
|
||||
from json import JSONDecodeError
|
||||
from time import time
|
||||
from typing import NamedTuple
|
||||
|
||||
from src import shared
|
||||
from src.game import Game
|
||||
from src.importer.sources.location import Location
|
||||
from src.importer.sources.location import Location, LocationSubPath
|
||||
from src.importer.sources.source import Source, SourceIterationResult, SourceIterable
|
||||
|
||||
|
||||
@@ -50,7 +51,7 @@ class LegendarySourceIterable(SourceIterable):
|
||||
data = {}
|
||||
|
||||
# Get additional metadata from file (optional)
|
||||
metadata_file = self.source.config_location["metadata"] / f"{app_name}.json"
|
||||
metadata_file = self.source.locations.config["metadata"] / f"{app_name}.json"
|
||||
try:
|
||||
metadata = json.load(metadata_file.open())
|
||||
values["developer"] = metadata["metadata"]["developer"]
|
||||
@@ -66,7 +67,7 @@ class LegendarySourceIterable(SourceIterable):
|
||||
|
||||
def __iter__(self):
|
||||
# Open library
|
||||
file = self.source.config_location["installed.json"]
|
||||
file = self.source.locations.config["installed.json"]
|
||||
try:
|
||||
library: dict = json.load(file.open())
|
||||
except (JSONDecodeError, OSError):
|
||||
@@ -88,20 +89,27 @@ class LegendarySourceIterable(SourceIterable):
|
||||
yield result
|
||||
|
||||
|
||||
class LegendaryLocations(NamedTuple):
|
||||
config: Location
|
||||
|
||||
|
||||
class LegendarySource(Source):
|
||||
name = _("Legendary")
|
||||
executable_format = "legendary launch {app_name}"
|
||||
available_on = {"linux"}
|
||||
|
||||
iterable_class = LegendarySourceIterable
|
||||
config_location: Location = Location(
|
||||
|
||||
locations = LegendaryLocations(
|
||||
Location(
|
||||
schema_key="legendary-location",
|
||||
candidates=(
|
||||
shared.config_dir / "legendary",
|
||||
shared.home / ".config" / "legendary",
|
||||
),
|
||||
paths={
|
||||
"installed.json": (False, "installed.json"),
|
||||
"metadata": (True, "metadata"),
|
||||
"installed.json": LocationSubPath("installed.json"),
|
||||
"metadata": LocationSubPath("metadata", True),
|
||||
},
|
||||
invalid_subtitle=Location.CONFIG_INVALID_SUBTITLE,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Callable, Mapping, Iterable
|
||||
from typing import Mapping, Iterable, NamedTuple
|
||||
from os import PathLike
|
||||
|
||||
from src import shared
|
||||
|
||||
PathSegment = str | PathLike | Path
|
||||
PathSegments = Iterable[PathSegment]
|
||||
Candidate = PathSegments | Callable[[], PathSegments]
|
||||
Candidate = PathSegments
|
||||
|
||||
|
||||
class LocationSubPath(NamedTuple):
|
||||
segment: PathSegment
|
||||
is_directory: bool = False
|
||||
|
||||
|
||||
class UnresolvableLocationError(Exception):
|
||||
@@ -24,31 +29,42 @@ class Location:
|
||||
* When resolved, the schema is updated with the picked chosen
|
||||
"""
|
||||
|
||||
# The variable is the name of the source
|
||||
CACHE_INVALID_SUBTITLE = _("Select the {} cache directory.")
|
||||
# The variable is the name of the source
|
||||
CONFIG_INVALID_SUBTITLE = _("Select the {} configuration directory.")
|
||||
# The variable is the name of the source
|
||||
DATA_INVALID_SUBTITLE = _("Select the {} data directory.")
|
||||
|
||||
schema_key: str
|
||||
candidates: Iterable[Candidate]
|
||||
paths: Mapping[str, tuple[bool, PathSegments]]
|
||||
paths: Mapping[str, LocationSubPath]
|
||||
invalid_subtitle: str
|
||||
|
||||
root: Path = None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
schema_key: str,
|
||||
candidates: Iterable[Candidate],
|
||||
paths: Mapping[str, tuple[bool, PathSegments]],
|
||||
paths: Mapping[str, LocationSubPath],
|
||||
invalid_subtitle: str,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.schema_key = schema_key
|
||||
self.candidates = candidates
|
||||
self.paths = paths
|
||||
self.invalid_subtitle = invalid_subtitle
|
||||
|
||||
def check_candidate(self, candidate: Path) -> bool:
|
||||
"""Check if a candidate root has the necessary files and directories"""
|
||||
for type_is_dir, subpath in self.paths.values():
|
||||
subpath = Path(candidate) / Path(subpath)
|
||||
if type_is_dir:
|
||||
if not subpath.is_dir():
|
||||
for segment, is_directory in self.paths.values():
|
||||
path = Path(candidate) / segment
|
||||
if is_directory:
|
||||
if not path.is_dir():
|
||||
return False
|
||||
else:
|
||||
if not subpath.is_file():
|
||||
if not path.is_file():
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -81,4 +97,4 @@ class Location:
|
||||
def __getitem__(self, key: str):
|
||||
"""Get the computed path from its key for the location"""
|
||||
self.resolve()
|
||||
return self.root / self.paths[key][1]
|
||||
return self.root / self.paths[key].segment
|
||||
|
||||
@@ -20,10 +20,11 @@
|
||||
from shutil import rmtree
|
||||
from sqlite3 import connect
|
||||
from time import time
|
||||
from typing import NamedTuple
|
||||
|
||||
from src import shared
|
||||
from src.game import Game
|
||||
from src.importer.sources.location import Location
|
||||
from src.importer.sources.location import Location, LocationSubPath
|
||||
from src.importer.sources.source import SourceIterable, URLExecutableSource
|
||||
from src.utils.sqlite import copy_db
|
||||
|
||||
@@ -51,7 +52,7 @@ class LutrisSourceIterable(SourceIterable):
|
||||
"import_steam": shared.schema.get_boolean("lutris-import-steam"),
|
||||
"import_flatpak": shared.schema.get_boolean("lutris-import-flatpak"),
|
||||
}
|
||||
db_path = copy_db(self.source.data_location["pga.db"])
|
||||
db_path = copy_db(self.source.locations.config["pga.db"])
|
||||
connection = connect(db_path)
|
||||
cursor = connection.execute(request, params)
|
||||
|
||||
@@ -73,7 +74,7 @@ class LutrisSourceIterable(SourceIterable):
|
||||
game = Game(values)
|
||||
|
||||
# Get official image path
|
||||
image_path = self.source.cache_location["coverart"] / f"{row[2]}.jpg"
|
||||
image_path = self.source.locations.cache["coverart"] / f"{row[2]}.jpg"
|
||||
additional_data = {"local_image_path": image_path}
|
||||
|
||||
# Produce game
|
||||
@@ -83,6 +84,11 @@ class LutrisSourceIterable(SourceIterable):
|
||||
rmtree(str(db_path.parent))
|
||||
|
||||
|
||||
class LutrisLocations(NamedTuple):
|
||||
config: Location
|
||||
cache: Location
|
||||
|
||||
|
||||
class LutrisSource(URLExecutableSource):
|
||||
"""Generic Lutris source"""
|
||||
|
||||
@@ -91,9 +97,10 @@ class LutrisSource(URLExecutableSource):
|
||||
url_format = "lutris:rungameid/{game_id}"
|
||||
available_on = {"linux"}
|
||||
|
||||
# FIXME possible bug: location picks ~/.var... and cache_lcoation picks ~/.local...
|
||||
# FIXME possible bug: config picks ~/.var... and cache picks ~/.local...
|
||||
|
||||
data_location = Location(
|
||||
locations = LutrisLocations(
|
||||
Location(
|
||||
schema_key="lutris-location",
|
||||
candidates=(
|
||||
shared.flatpak_dir / "net.lutris.Lutris" / "data" / "lutris",
|
||||
@@ -103,9 +110,9 @@ class LutrisSource(URLExecutableSource):
|
||||
paths={
|
||||
"pga.db": (False, "pga.db"),
|
||||
},
|
||||
)
|
||||
|
||||
cache_location = Location(
|
||||
invalid_subtitle=Location.DATA_INVALID_SUBTITLE,
|
||||
),
|
||||
Location(
|
||||
schema_key="lutris-cache-location",
|
||||
candidates=(
|
||||
shared.flatpak_dir / "net.lutris.Lutris" / "cache" / "lutris",
|
||||
@@ -113,8 +120,10 @@ class LutrisSource(URLExecutableSource):
|
||||
shared.home / ".cache" / "lutris",
|
||||
),
|
||||
paths={
|
||||
"coverart": (True, "coverart"),
|
||||
"coverart": LocationSubPath("coverart", True),
|
||||
},
|
||||
invalid_subtitle=Location.CACHE_INVALID_SUBTITLE,
|
||||
),
|
||||
)
|
||||
|
||||
@property
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
|
||||
import sys
|
||||
from abc import abstractmethod
|
||||
from collections.abc import Iterable, Iterator
|
||||
from typing import Any, Generator, Optional
|
||||
from collections.abc import Iterable
|
||||
from typing import Any, Generator, Collection
|
||||
|
||||
from src.game import Game
|
||||
from src.importer.sources.location import Location
|
||||
@@ -54,10 +54,8 @@ class Source(Iterable):
|
||||
name: str
|
||||
variant: str = None
|
||||
available_on: set[str] = set()
|
||||
data_location: Optional[Location] = None
|
||||
cache_location: Optional[Location] = None
|
||||
config_location: Optional[Location] = None
|
||||
iterable_class: type[SourceIterable]
|
||||
locations: Collection[Location]
|
||||
|
||||
@property
|
||||
def full_name(self) -> str:
|
||||
|
||||
@@ -22,11 +22,11 @@ import logging
|
||||
import re
|
||||
from pathlib import Path
|
||||
from time import time
|
||||
from typing import Iterable
|
||||
from typing import Iterable, NamedTuple
|
||||
|
||||
from src import shared
|
||||
from src.game import Game
|
||||
from src.importer.sources.location import Location
|
||||
from src.importer.sources.location import Location, LocationSubPath
|
||||
from src.importer.sources.source import SourceIterable, URLExecutableSource
|
||||
from src.utils.steam import SteamFileHelper, SteamInvalidManifestError
|
||||
|
||||
@@ -36,7 +36,7 @@ class SteamSourceIterable(SourceIterable):
|
||||
|
||||
def get_manifest_dirs(self) -> Iterable[Path]:
|
||||
"""Get dirs that contain steam app manifests"""
|
||||
libraryfolders_path = self.source.data_location["libraryfolders.vdf"]
|
||||
libraryfolders_path = self.source.locations.data["libraryfolders.vdf"]
|
||||
with open(libraryfolders_path, "r", encoding="utf-8") as file:
|
||||
contents = file.read()
|
||||
return [
|
||||
@@ -100,7 +100,7 @@ class SteamSourceIterable(SourceIterable):
|
||||
|
||||
# Add official cover image
|
||||
image_path = (
|
||||
self.source.data_location["librarycache"]
|
||||
self.source.locations.data["librarycache"]
|
||||
/ f"{appid}_library_600x900.jpg"
|
||||
)
|
||||
additional_data = {"local_image_path": image_path, "steam_appid": appid}
|
||||
@@ -109,13 +109,18 @@ class SteamSourceIterable(SourceIterable):
|
||||
yield (game, additional_data)
|
||||
|
||||
|
||||
class SteamLocations(NamedTuple):
|
||||
data: Location
|
||||
|
||||
|
||||
class SteamSource(URLExecutableSource):
|
||||
name = _("Steam")
|
||||
available_on = {"linux", "win32"}
|
||||
iterable_class = SteamSourceIterable
|
||||
url_format = "steam://rungameid/{game_id}"
|
||||
|
||||
data_location = Location(
|
||||
locations = SteamLocations(
|
||||
Location(
|
||||
schema_key="steam-location",
|
||||
candidates=(
|
||||
shared.home / ".steam" / "steam",
|
||||
@@ -124,7 +129,9 @@ class SteamSource(URLExecutableSource):
|
||||
shared.programfiles32_dir / "Steam",
|
||||
),
|
||||
paths={
|
||||
"libraryfolders.vdf": (False, "steamapps/libraryfolders.vdf"),
|
||||
"librarycache": (True, "appcache/librarycache"),
|
||||
"libraryfolders.vdf": LocationSubPath("steamapps/libraryfolders.vdf"),
|
||||
"librarycache": LocationSubPath("appcache/librarycache", True),
|
||||
},
|
||||
invalid_subtitle=Location.DATA_INVALID_SUBTITLE,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -262,19 +262,19 @@ class PreferencesWindow(Adw.PreferencesWindow):
|
||||
subtitle = re.sub("/run/user/\\d*/doc/.*/", "", str(path))
|
||||
action_row.set_subtitle(subtitle)
|
||||
|
||||
def resolve_locations(self, source):
|
||||
def resolve_locations(self, source: Source):
|
||||
"""Resolve locations and add a warning if location cannot be found"""
|
||||
|
||||
def clear_warning_selection(_widget, label):
|
||||
label.select_region(-1, -1)
|
||||
|
||||
for location_name in ("data", "config", "cache"):
|
||||
for location_name, location in source.locations._asdict().items():
|
||||
action_row = getattr(self, f"{source.id}_{location_name}_action_row", None)
|
||||
if not action_row:
|
||||
continue
|
||||
|
||||
try:
|
||||
getattr(source, f"{location_name}_location", None).resolve()
|
||||
location.resolve()
|
||||
|
||||
except UnresolvableLocationError:
|
||||
popover = Gtk.Popover(
|
||||
@@ -325,10 +325,14 @@ class PreferencesWindow(Adw.PreferencesWindow):
|
||||
return
|
||||
|
||||
# Good picked location
|
||||
location = getattr(source, f"{location_name}_location")
|
||||
location = getattr(source.locations, location_name)
|
||||
if location.check_candidate(path):
|
||||
# Set the schema
|
||||
infix = "-cache" if location_name == "cache" else ""
|
||||
match location_name:
|
||||
case "config" | "data":
|
||||
infix = ""
|
||||
case _:
|
||||
infix = f"-{location_name}"
|
||||
key = f"{source.id}{infix}-location"
|
||||
value = str(path)
|
||||
shared.schema.set_string(key, value)
|
||||
@@ -347,20 +351,10 @@ class PreferencesWindow(Adw.PreferencesWindow):
|
||||
# Bad picked location, inform user
|
||||
else:
|
||||
title = _("Invalid Directory")
|
||||
match location_name:
|
||||
case "cache":
|
||||
# The variable is the name of the source
|
||||
subtitle_format = _("Select the {} cache directory.")
|
||||
case "config":
|
||||
# The variable is the name of the source
|
||||
subtitle_format = _("Select the {} configuration directory.")
|
||||
case "data":
|
||||
# The variable is the name of the source
|
||||
subtitle_format = _("Select the {} data directory.")
|
||||
dialog = create_dialog(
|
||||
self,
|
||||
title,
|
||||
subtitle_format.format(source.name),
|
||||
location.invalid_subtitle.format(source.name),
|
||||
"choose_folder",
|
||||
_("Set Location"),
|
||||
)
|
||||
@@ -381,10 +375,12 @@ class PreferencesWindow(Adw.PreferencesWindow):
|
||||
)
|
||||
|
||||
# Connect dir picker buttons
|
||||
for location in ("data", "config", "cache"):
|
||||
button = getattr(self, f"{source.id}_{location}_file_chooser_button", None)
|
||||
for location_name in source.locations._asdict():
|
||||
button = getattr(
|
||||
self, f"{source.id}_{location_name}_file_chooser_button", None
|
||||
)
|
||||
if button is not None:
|
||||
button.connect("clicked", self.choose_folder, set_dir, location)
|
||||
button.connect("clicked", self.choose_folder, set_dir, location_name)
|
||||
|
||||
# Set the source row subtitles
|
||||
self.resolve_locations(source)
|
||||
|
||||
Reference in New Issue
Block a user