- Changed source additional data to dict - Moved local cover saving into a manager - Added stub for itch cover manager
125 lines
3.8 KiB
Python
125 lines
3.8 KiB
Python
import re
|
|
from pathlib import Path
|
|
from time import time
|
|
from typing import Iterable
|
|
|
|
from src import shared
|
|
from src.game import Game
|
|
from src.importer.sources.source import (
|
|
LinuxSource,
|
|
Source,
|
|
SourceIterationResult,
|
|
SourceIterator,
|
|
WindowsSource,
|
|
)
|
|
from src.utils.decorators import replaced_by_env_path, replaced_by_path
|
|
from src.utils.steam import SteamHelper, SteamInvalidManifestError
|
|
|
|
|
|
class SteamSourceIterator(SourceIterator):
|
|
source: "SteamSource"
|
|
|
|
def get_manifest_dirs(self) -> Iterable[Path]:
|
|
"""Get dirs that contain steam app manifests"""
|
|
libraryfolders_path = self.source.location / "steamapps" / "libraryfolders.vdf"
|
|
with open(libraryfolders_path, "r") as file:
|
|
contents = file.read()
|
|
return [
|
|
Path(path) / "steamapps"
|
|
for path in re.findall('"path"\s+"(.*)"\n', contents, re.IGNORECASE)
|
|
]
|
|
|
|
def get_manifests(self) -> Iterable[Path]:
|
|
"""Get app manifests"""
|
|
manifests = set()
|
|
for steamapps_dir in self.get_manifest_dirs():
|
|
if not steamapps_dir.is_dir():
|
|
continue
|
|
manifests.update(
|
|
[
|
|
manifest
|
|
for manifest in steamapps_dir.glob("appmanifest_*.acf")
|
|
if manifest.is_file()
|
|
]
|
|
)
|
|
return manifests
|
|
|
|
def generator_builder(self) -> SourceIterationResult:
|
|
"""Generator method producing games"""
|
|
appid_cache = set()
|
|
manifests = self.get_manifests()
|
|
for manifest in manifests:
|
|
# Get metadata from manifest
|
|
steam = SteamHelper()
|
|
try:
|
|
local_data = steam.get_manifest_data(manifest)
|
|
except (OSError, SteamInvalidManifestError):
|
|
continue
|
|
|
|
# Skip non installed games
|
|
INSTALLED_MASK: int = 4
|
|
if not int(local_data["stateflags"]) & INSTALLED_MASK:
|
|
continue
|
|
|
|
# Skip duplicate appids
|
|
appid = local_data["appid"]
|
|
if appid in appid_cache:
|
|
continue
|
|
appid_cache.add(appid)
|
|
|
|
# Build game from local data
|
|
values = {
|
|
"version": shared.SPEC_VERSION,
|
|
"added": int(time()),
|
|
"name": local_data["name"],
|
|
"source": self.source.id,
|
|
"game_id": self.source.game_id_format.format(game_id=appid),
|
|
"executable": self.source.executable_format.format(game_id=appid),
|
|
}
|
|
game = Game(values, allow_side_effects=False)
|
|
|
|
# Add official cover image
|
|
image_path = (
|
|
self.source.location
|
|
/ "appcache"
|
|
/ "librarycache"
|
|
/ f"{appid}_library_600x900.jpg"
|
|
)
|
|
additional_data = {"local_image_path": image_path}
|
|
|
|
# Produce game
|
|
yield (game, additional_data)
|
|
|
|
|
|
class SteamSource(Source):
|
|
name = "Steam"
|
|
location_key = "steam-location"
|
|
|
|
def __iter__(self):
|
|
return SteamSourceIterator(source=self)
|
|
|
|
|
|
class SteamLinuxSource(SteamSource, LinuxSource):
|
|
variant = "linux"
|
|
executable_format = "xdg-open steam://rungameid/{game_id}"
|
|
|
|
@property
|
|
@SteamSource.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/")
|
|
@replaced_by_path("~/.local/share/Steam/")
|
|
def location(self):
|
|
raise FileNotFoundError()
|
|
|
|
|
|
class SteamWindowsSource(SteamSource, WindowsSource):
|
|
variant = "windows"
|
|
executable_format = "start steam://rungameid/{game_id}"
|
|
|
|
@property
|
|
@SteamSource.replaced_by_schema_key()
|
|
@replaced_by_env_path("programfiles(x86)", "Steam")
|
|
def location(self):
|
|
raise FileNotFoundError()
|