Files
cartridges/src/importer/sources/steam_source.py
GeoffreyCoulaud 5dc6ec899a 🎨 Various changes
- Changed source additional data to dict
- Moved local cover saving into a manager
- Added stub for itch cover manager
2023-06-07 15:00:42 +02:00

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