steam-client 3.0.0.dev0__tar.gz → 4.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- steam_client-4.1.0/PKG-INFO +145 -0
- steam_client-4.1.0/README.md +128 -0
- {steam_client-3.0.0.dev0 → steam_client-4.1.0}/pyproject.toml +1 -31
- steam_client-4.1.0/steam_client/__init__.py +3 -0
- {steam_client-3.0.0.dev0 → steam_client-4.1.0}/steam_client/app.py +1 -7
- {steam_client-3.0.0.dev0 → steam_client-4.1.0}/steam_client/commands.py +12 -24
- {steam_client-3.0.0.dev0 → steam_client-4.1.0}/steam_client/game.py +25 -21
- {steam_client-3.0.0.dev0 → steam_client-4.1.0}/steam_client/library.py +22 -20
- steam_client-4.1.0/steam_client/library_folder.py +17 -0
- {steam_client-3.0.0.dev0 → steam_client-4.1.0}/steam_client/login_users.py +11 -12
- {steam_client-3.0.0.dev0 → steam_client-4.1.0}/steam_client/shortcut.py +29 -11
- {steam_client-3.0.0.dev0 → steam_client-4.1.0}/steam_client/steam.py +1 -1
- steam_client-4.1.0/steam_client.egg-info/PKG-INFO +145 -0
- {steam_client-3.0.0.dev0 → steam_client-4.1.0}/steam_client.egg-info/SOURCES.txt +3 -2
- {steam_client-3.0.0.dev0 → steam_client-4.1.0}/tests/test_commands.py +4 -12
- steam_client-4.1.0/tests/test_game.py +108 -0
- steam_client-4.1.0/tests/test_library.py +220 -0
- {steam_client-3.0.0.dev0 → steam_client-4.1.0}/tests/test_login_users.py +5 -5
- {steam_client-3.0.0.dev0 → steam_client-4.1.0}/tests/test_shortcut.py +14 -15
- steam_client-3.0.0.dev0/PKG-INFO +0 -16
- steam_client-3.0.0.dev0/steam_client/__init__.py +0 -0
- steam_client-3.0.0.dev0/steam_client/library_directory.py +0 -18
- steam_client-3.0.0.dev0/steam_client/web_api.py +0 -24
- steam_client-3.0.0.dev0/steam_client.egg-info/PKG-INFO +0 -16
- steam_client-3.0.0.dev0/tests/test_game.py +0 -61
- {steam_client-3.0.0.dev0 → steam_client-4.1.0}/LICENSE +0 -0
- {steam_client-3.0.0.dev0 → steam_client-4.1.0}/setup.cfg +0 -0
- {steam_client-3.0.0.dev0 → steam_client-4.1.0}/setup.py +0 -0
- {steam_client-3.0.0.dev0 → steam_client-4.1.0}/steam_client/py.typed +0 -0
- {steam_client-3.0.0.dev0 → steam_client-4.1.0}/steam_client/utils.py +0 -0
- {steam_client-3.0.0.dev0 → steam_client-4.1.0}/steam_client.egg-info/dependency_links.txt +0 -0
- {steam_client-3.0.0.dev0 → steam_client-4.1.0}/steam_client.egg-info/requires.txt +0 -0
- {steam_client-3.0.0.dev0 → steam_client-4.1.0}/steam_client.egg-info/top_level.txt +0 -0
- {steam_client-3.0.0.dev0 → steam_client-4.1.0}/tests/test_steam.py +0 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: steam-client
|
|
3
|
+
Version: 4.1.0
|
|
4
|
+
Summary: Steam client library for Python
|
|
5
|
+
Author-email: William McAllister <dev.garulf@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Source, https://github.com/garulf/steam-client
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: vdf>=3.4
|
|
15
|
+
Requires-Dist: pycrc>=0.10.0
|
|
16
|
+
Dynamic: license-file
|
|
17
|
+
|
|
18
|
+
# Steam Client
|
|
19
|
+
|
|
20
|
+
A Python library for interacting with a locally installed Steam client — enumerate your game library/non-steam games, read game metadata and artwork paths, and send Steam URI commands.
|
|
21
|
+
|
|
22
|
+
[](docs/index.md)
|
|
23
|
+
|
|
24
|
+
[](https://www.buymeacoffee.com/garulf)
|
|
25
|
+
[](https://ko-fi.com/garulf)
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
Requires Python 3.8+.
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install steam-client
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Or install from source:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install -e .
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Quickstart
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from steam_client.steam import Steam
|
|
49
|
+
|
|
50
|
+
steam = Steam() # Uses default path: C:\Program Files (x86)\Steam
|
|
51
|
+
|
|
52
|
+
# On Windows, auto-detect path from registry:
|
|
53
|
+
from steam_client.utils import steam_from_registry
|
|
54
|
+
steam = steam_from_registry()
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Common Operations
|
|
60
|
+
|
|
61
|
+
### List all installed games
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
for game in steam.library.games():
|
|
65
|
+
print(game.appid, game.name)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Find a game
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
game = steam.library.game_by_id("440") # by app ID
|
|
72
|
+
game = steam.library.game_by_name("Portal 2") # by name (case-insensitive)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Artwork paths
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
game.header # Path to header image (460×215 jpg)
|
|
79
|
+
game.grid # Path to grid image (600×900 jpg)
|
|
80
|
+
game.hero # Path to hero image
|
|
81
|
+
game.icon # Path to icon file, or None
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Launch and manage games via Steam URI commands
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from steam_client.commands import Commands, SteamWindows
|
|
88
|
+
|
|
89
|
+
commands = Commands()
|
|
90
|
+
commands.run_game_id("440") # Launch a game
|
|
91
|
+
commands.store("440") # Open store page
|
|
92
|
+
commands.install("440") # Prompt install
|
|
93
|
+
commands.uninstall("440") # Prompt uninstall
|
|
94
|
+
commands.open(SteamWindows.FRIENDS) # Open a Steam window
|
|
95
|
+
commands.open_url("https://store.steampowered.com")
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Or launch a game directly from a `SteamGame` object:
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
game.run()
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Users and shortcuts
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
for user in steam.users:
|
|
108
|
+
print(user.user.data.PersonaName)
|
|
109
|
+
|
|
110
|
+
for shortcut in steam.library.shortcuts():
|
|
111
|
+
print(shortcut.appname)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Platform Notes
|
|
115
|
+
|
|
116
|
+
| Platform | Default path | Registry helper |
|
|
117
|
+
|----------|--------------------------------|-------------------------|
|
|
118
|
+
| Windows | `C:\Program Files (x86)\Steam` | `steam_from_registry()` |
|
|
119
|
+
| Linux | `~/.local/share/steam` | Not available |
|
|
120
|
+
|
|
121
|
+
Pass a custom path to override the default:
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
steam = Steam("/custom/steam/path")
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Requires read access to local Steam config files (`libraryfolders.vdf`, `shortcuts.vdf`, appmanifest files).
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Development
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
pytest # Run tests
|
|
135
|
+
flake8 steam_client # Lint
|
|
136
|
+
mypy steam_client # Type check
|
|
137
|
+
mkdocs serve # Preview docs locally
|
|
138
|
+
mkdocs build --strict # Validate docs build
|
|
139
|
+
tox # Run all environments (lint, type, docs, py311–314)
|
|
140
|
+
|
|
141
|
+
bump-my-version show current_version # Show current version
|
|
142
|
+
bump-my-version bump patch # 4.0.0 → 4.0.1
|
|
143
|
+
bump-my-version bump minor # 4.0.0 → 4.1.0
|
|
144
|
+
bump-my-version bump major # 4.0.0 → 5.0.0
|
|
145
|
+
```
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Steam Client
|
|
2
|
+
|
|
3
|
+
A Python library for interacting with a locally installed Steam client — enumerate your game library/non-steam games, read game metadata and artwork paths, and send Steam URI commands.
|
|
4
|
+
|
|
5
|
+
[](docs/index.md)
|
|
6
|
+
|
|
7
|
+
[](https://www.buymeacoffee.com/garulf)
|
|
8
|
+
[](https://ko-fi.com/garulf)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
Requires Python 3.8+.
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install steam-client
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Or install from source:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install -e .
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Quickstart
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from steam_client.steam import Steam
|
|
32
|
+
|
|
33
|
+
steam = Steam() # Uses default path: C:\Program Files (x86)\Steam
|
|
34
|
+
|
|
35
|
+
# On Windows, auto-detect path from registry:
|
|
36
|
+
from steam_client.utils import steam_from_registry
|
|
37
|
+
steam = steam_from_registry()
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Common Operations
|
|
43
|
+
|
|
44
|
+
### List all installed games
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
for game in steam.library.games():
|
|
48
|
+
print(game.appid, game.name)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Find a game
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
game = steam.library.game_by_id("440") # by app ID
|
|
55
|
+
game = steam.library.game_by_name("Portal 2") # by name (case-insensitive)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Artwork paths
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
game.header # Path to header image (460×215 jpg)
|
|
62
|
+
game.grid # Path to grid image (600×900 jpg)
|
|
63
|
+
game.hero # Path to hero image
|
|
64
|
+
game.icon # Path to icon file, or None
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Launch and manage games via Steam URI commands
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from steam_client.commands import Commands, SteamWindows
|
|
71
|
+
|
|
72
|
+
commands = Commands()
|
|
73
|
+
commands.run_game_id("440") # Launch a game
|
|
74
|
+
commands.store("440") # Open store page
|
|
75
|
+
commands.install("440") # Prompt install
|
|
76
|
+
commands.uninstall("440") # Prompt uninstall
|
|
77
|
+
commands.open(SteamWindows.FRIENDS) # Open a Steam window
|
|
78
|
+
commands.open_url("https://store.steampowered.com")
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Or launch a game directly from a `SteamGame` object:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
game.run()
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Users and shortcuts
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
for user in steam.users:
|
|
91
|
+
print(user.user.data.PersonaName)
|
|
92
|
+
|
|
93
|
+
for shortcut in steam.library.shortcuts():
|
|
94
|
+
print(shortcut.appname)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Platform Notes
|
|
98
|
+
|
|
99
|
+
| Platform | Default path | Registry helper |
|
|
100
|
+
|----------|--------------------------------|-------------------------|
|
|
101
|
+
| Windows | `C:\Program Files (x86)\Steam` | `steam_from_registry()` |
|
|
102
|
+
| Linux | `~/.local/share/steam` | Not available |
|
|
103
|
+
|
|
104
|
+
Pass a custom path to override the default:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
steam = Steam("/custom/steam/path")
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Requires read access to local Steam config files (`libraryfolders.vdf`, `shortcuts.vdf`, appmanifest files).
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Development
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
pytest # Run tests
|
|
118
|
+
flake8 steam_client # Lint
|
|
119
|
+
mypy steam_client # Type check
|
|
120
|
+
mkdocs serve # Preview docs locally
|
|
121
|
+
mkdocs build --strict # Validate docs build
|
|
122
|
+
tox # Run all environments (lint, type, docs, py311–314)
|
|
123
|
+
|
|
124
|
+
bump-my-version show current_version # Show current version
|
|
125
|
+
bump-my-version bump patch # 4.0.0 → 4.0.1
|
|
126
|
+
bump-my-version bump minor # 4.0.0 → 4.1.0
|
|
127
|
+
bump-my-version bump major # 4.0.0 → 5.0.0
|
|
128
|
+
```
|
|
@@ -6,7 +6,7 @@ name = "steam-client"
|
|
|
6
6
|
authors = [
|
|
7
7
|
{name = "William McAllister", email = "dev.garulf@gmail.com"}
|
|
8
8
|
]
|
|
9
|
-
version = "
|
|
9
|
+
version = "4.1.0"
|
|
10
10
|
description = "Steam client library for Python"
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
requires-python = ">=3.8"
|
|
@@ -30,34 +30,4 @@ packages = ["steam_client"]
|
|
|
30
30
|
[tool.setuptools.package-data]
|
|
31
31
|
"steam_client" = ["py.typed"]
|
|
32
32
|
|
|
33
|
-
[tool.bumpversion]
|
|
34
|
-
current_version = "3.0.0-dev.0"
|
|
35
|
-
search = "version = \"{current_version}\""
|
|
36
|
-
replace = "version = \"{new_version}\""
|
|
37
|
-
parse = """(?x)
|
|
38
|
-
(?P<major>[0-9]+)
|
|
39
|
-
\\.(?P<minor>[0-9]+)
|
|
40
|
-
\\.(?P<patch>[0-9]+)
|
|
41
|
-
(?:
|
|
42
|
-
-(?P<label>dev|rc|release)
|
|
43
|
-
(?:.(?P<label_n>[0-9]+))?
|
|
44
|
-
)?
|
|
45
|
-
"""
|
|
46
|
-
serialize = [
|
|
47
|
-
"{major}.{minor}.{patch}-{label}.{label_n}",
|
|
48
|
-
"{major}.{minor}.{patch}",
|
|
49
|
-
]
|
|
50
|
-
tag = true
|
|
51
|
-
allow_dirty = false
|
|
52
|
-
commit = true
|
|
53
|
-
|
|
54
|
-
[tool.bumpversion.parts.label]
|
|
55
|
-
optional_value = "release"
|
|
56
|
-
values =[
|
|
57
|
-
"dev",
|
|
58
|
-
"rc",
|
|
59
|
-
"release",
|
|
60
|
-
]
|
|
61
33
|
|
|
62
|
-
[[tool.bumpversion.files]]
|
|
63
|
-
filename = "pyproject.toml"
|
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
from abc import ABC, abstractmethod
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
5
|
-
|
|
6
4
|
|
|
7
5
|
from .commands import Commands
|
|
8
6
|
|
|
9
|
-
if TYPE_CHECKING:
|
|
10
|
-
from .steam import Steam
|
|
11
|
-
|
|
12
7
|
|
|
13
8
|
class App(ABC):
|
|
14
9
|
"""Abstract base class for all Steam apps."""
|
|
15
10
|
|
|
16
|
-
def __init__(self
|
|
17
|
-
self._steam = steam
|
|
11
|
+
def __init__(self):
|
|
18
12
|
self._commands = Commands()
|
|
19
13
|
|
|
20
14
|
@property
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
from typing import List
|
|
2
1
|
import webbrowser
|
|
3
2
|
from enum import StrEnum
|
|
4
3
|
|
|
5
|
-
|
|
4
|
+
|
|
5
|
+
def _create_uri(*segments: str) -> str:
|
|
6
|
+
return f'steam://{"/".join(segments)}'
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class SteamWindows(StrEnum):
|
|
@@ -21,49 +22,36 @@ class SteamWindows(StrEnum):
|
|
|
21
22
|
CONSOLE = 'console'
|
|
22
23
|
|
|
23
24
|
|
|
24
|
-
class Command():
|
|
25
|
-
|
|
26
|
-
def __init__(self, scheme: str):
|
|
27
|
-
self._scheme = scheme
|
|
28
|
-
|
|
29
|
-
def _create_uri(self, path: List[str], endpoint: str) -> str:
|
|
30
|
-
return f'{self._scheme}://{"/".join(path)}/{endpoint}'
|
|
31
|
-
|
|
32
|
-
def __call__(self, path: List[str], endpoint: str) -> None:
|
|
33
|
-
"""Executes the command with the specified endpoint."""
|
|
34
|
-
webbrowser.open(self._create_uri(path, endpoint))
|
|
35
|
-
|
|
36
|
-
|
|
37
25
|
class Commands:
|
|
38
26
|
"""A collection of commands for the Steam client."""
|
|
39
27
|
|
|
40
|
-
def
|
|
41
|
-
|
|
28
|
+
def _open(self, *segments: str) -> None:
|
|
29
|
+
webbrowser.open(_create_uri(*segments))
|
|
42
30
|
|
|
43
31
|
def run_game_id(self, app_id: str) -> None:
|
|
44
32
|
"""Launches game with the specified ID in the Steam client."""
|
|
45
|
-
self.
|
|
33
|
+
self._open('rungameid', app_id)
|
|
46
34
|
|
|
47
35
|
def store(self, app_id: str) -> None:
|
|
48
36
|
"""Opens the game's store page in the Steam client."""
|
|
49
|
-
self.
|
|
37
|
+
self._open('store', app_id)
|
|
50
38
|
|
|
51
39
|
def install(self, app_id: str) -> None:
|
|
52
40
|
"""Opens the game's install prompt in the Steam client."""
|
|
53
|
-
self.
|
|
41
|
+
self._open('install', app_id)
|
|
54
42
|
|
|
55
43
|
def uninstall(self, app_id: str) -> None:
|
|
56
44
|
"""Opens the game's uninstall prompt in the Steam client."""
|
|
57
|
-
self.
|
|
45
|
+
self._open('uninstall', app_id)
|
|
58
46
|
|
|
59
47
|
def update_news(self, app_id: str) -> None:
|
|
60
48
|
"""Opens the game's update news in the Steam client."""
|
|
61
|
-
self.
|
|
49
|
+
self._open('updatenews', app_id)
|
|
62
50
|
|
|
63
51
|
def open(self, window: SteamWindows) -> None:
|
|
64
52
|
"""Opens the specified window in the Steam client."""
|
|
65
|
-
self.
|
|
53
|
+
self._open('open', window)
|
|
66
54
|
|
|
67
55
|
def open_url(self, url: str) -> None:
|
|
68
56
|
"""Opens the specified URL in the Steam client."""
|
|
69
|
-
self.
|
|
57
|
+
self._open('openurl', url)
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
import functools
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
5
4
|
|
|
6
5
|
import vdf # type: ignore
|
|
7
6
|
|
|
8
|
-
if TYPE_CHECKING:
|
|
9
|
-
from .steam import Steam
|
|
10
|
-
|
|
11
7
|
from .app import App
|
|
12
8
|
|
|
13
9
|
|
|
@@ -22,19 +18,20 @@ ASSETS = frozenset({
|
|
|
22
18
|
})
|
|
23
19
|
|
|
24
20
|
|
|
25
|
-
class
|
|
21
|
+
class Game(App):
|
|
26
22
|
"""Represents a Steam game."""
|
|
27
23
|
|
|
28
|
-
def __init__(self,
|
|
24
|
+
def __init__(self, library_cache_path: Path, library_path: str, appid: str):
|
|
25
|
+
self._library_cache_path = Path(library_cache_path)
|
|
29
26
|
self.library_path = library_path
|
|
30
27
|
self._appid = appid
|
|
31
28
|
self._icon: Path | None = None
|
|
32
29
|
self._name: str | None = None
|
|
33
|
-
super().__init__(
|
|
30
|
+
super().__init__()
|
|
34
31
|
|
|
35
32
|
def __repr__(self) -> str:
|
|
36
33
|
return (
|
|
37
|
-
f'Game(
|
|
34
|
+
f'Game(library_cache_path={self._library_cache_path.__repr__()}, '
|
|
38
35
|
f'library_path={self.library_path.__repr__()}, '
|
|
39
36
|
f'appid={self.appid.__repr__()})'
|
|
40
37
|
)
|
|
@@ -42,41 +39,44 @@ class SteamGame(App):
|
|
|
42
39
|
@property
|
|
43
40
|
def asset_dir(self) -> Path:
|
|
44
41
|
"""Returns the path to the game's asset directory."""
|
|
45
|
-
return
|
|
42
|
+
return self._library_cache_path.joinpath(self.appid)
|
|
46
43
|
|
|
47
44
|
@property
|
|
48
45
|
def icon(self) -> Path | None:
|
|
49
46
|
"""Returns the path to the icon image."""
|
|
50
47
|
if self._icon is None:
|
|
51
|
-
|
|
52
|
-
(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
48
|
+
try:
|
|
49
|
+
self._icon = next(
|
|
50
|
+
(
|
|
51
|
+
asset
|
|
52
|
+
for asset in self.asset_dir.iterdir()
|
|
53
|
+
if asset.is_file() and asset.name not in ASSETS
|
|
54
|
+
),
|
|
55
|
+
None,
|
|
56
|
+
)
|
|
57
|
+
except (FileNotFoundError, NotADirectoryError):
|
|
58
|
+
return None
|
|
59
59
|
return self._icon
|
|
60
60
|
|
|
61
61
|
@property
|
|
62
62
|
def header(self) -> Path:
|
|
63
63
|
"""Returns the path to the header image."""
|
|
64
|
-
return
|
|
64
|
+
return self._library_cache_path.joinpath(f'{self.appid}_header.jpg')
|
|
65
65
|
|
|
66
66
|
@property
|
|
67
67
|
def grid(self) -> Path:
|
|
68
68
|
"""Returns the path to the 600x900 grid image."""
|
|
69
|
-
return
|
|
69
|
+
return self._library_cache_path.joinpath(f'{self.appid}_library_600x900.jpg')
|
|
70
70
|
|
|
71
71
|
@property
|
|
72
72
|
def hero(self) -> Path:
|
|
73
73
|
"""Returns the path to the hero image."""
|
|
74
|
-
return
|
|
74
|
+
return self._library_cache_path.joinpath(f'{self.appid}_library_hero.jpg')
|
|
75
75
|
|
|
76
76
|
@property
|
|
77
77
|
def hero_blur(self) -> Path:
|
|
78
78
|
"""Returns the path to the blurred hero image."""
|
|
79
|
-
return
|
|
79
|
+
return self._library_cache_path.joinpath(f'{self.appid}_library_hero_blur.jpg')
|
|
80
80
|
|
|
81
81
|
@property
|
|
82
82
|
def manifest_path(self) -> Path:
|
|
@@ -115,6 +115,10 @@ class SteamGame(App):
|
|
|
115
115
|
"""Opens the game's store page in the Steam client."""
|
|
116
116
|
self._commands.store(self.appid)
|
|
117
117
|
|
|
118
|
+
def install(self):
|
|
119
|
+
"""Installs the game."""
|
|
120
|
+
self._commands.install(self.appid)
|
|
121
|
+
|
|
118
122
|
def uninstall(self):
|
|
119
123
|
"""Uninstalls the game."""
|
|
120
124
|
self._commands.uninstall(self.appid)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
import hashlib
|
|
3
|
-
from typing import TYPE_CHECKING, List, Optional, Union
|
|
3
|
+
from typing import TYPE_CHECKING, List, Optional, Union, Generator
|
|
4
4
|
|
|
5
5
|
import vdf # type: ignore
|
|
6
6
|
|
|
@@ -8,8 +8,8 @@ from steam_client.shortcut import Shortcut
|
|
|
8
8
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
10
10
|
from .steam import Steam
|
|
11
|
-
from .
|
|
12
|
-
from .game import
|
|
11
|
+
from .library_folder import LibraryFolder
|
|
12
|
+
from .game import Game
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class Library:
|
|
@@ -18,7 +18,7 @@ class Library:
|
|
|
18
18
|
def __init__(self, steam: 'Steam'):
|
|
19
19
|
self._steam = steam
|
|
20
20
|
self._libraries_hash: Optional[str] = None
|
|
21
|
-
self._libraries: List[
|
|
21
|
+
self._libraries: List[LibraryFolder] = []
|
|
22
22
|
|
|
23
23
|
def _hash_steam_libraries(self) -> str:
|
|
24
24
|
"""Returns the MD5 hash of the Steam library folders file."""
|
|
@@ -35,44 +35,46 @@ class Library:
|
|
|
35
35
|
libraries = vdf.load(f)
|
|
36
36
|
# The is not always formatted the same way, so we grab the first key
|
|
37
37
|
folder_key = list(libraries.keys())[0]
|
|
38
|
-
self._libraries = [
|
|
39
|
-
|
|
38
|
+
self._libraries = [LibraryFolder(self._steam.library_cache, libraries[folder_key][item]["path"],
|
|
39
|
+
libraries[folder_key][item]["apps"]) for item in libraries[folder_key]]
|
|
40
40
|
|
|
41
|
-
def libraries(self) ->
|
|
41
|
+
def libraries(self) -> Generator[LibraryFolder, None, None]:
|
|
42
42
|
"""Returns the Steam library folders."""
|
|
43
43
|
if self._is_updated():
|
|
44
44
|
self._libraries_hash = self._hash_steam_libraries()
|
|
45
45
|
self._update_libraries()
|
|
46
|
-
|
|
46
|
+
for library in self._libraries:
|
|
47
|
+
yield library
|
|
47
48
|
|
|
48
|
-
def games(self) ->
|
|
49
|
+
def games(self) -> Generator[Game, None, None]:
|
|
49
50
|
"""Returns the games from the Steam library."""
|
|
50
|
-
games = []
|
|
51
51
|
for library in self.libraries():
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
for game in library.get_games():
|
|
53
|
+
yield game
|
|
54
54
|
|
|
55
|
-
def game_by_id(self, appid: str) -> Optional[
|
|
55
|
+
def game_by_id(self, appid: str) -> Optional[Game]:
|
|
56
56
|
"""Returns the game with the specified ID."""
|
|
57
57
|
for game in self.games():
|
|
58
58
|
if game.appid == appid:
|
|
59
59
|
return game
|
|
60
60
|
return None
|
|
61
61
|
|
|
62
|
-
def game_by_name(self, name: str) -> Optional[
|
|
62
|
+
def game_by_name(self, name: str) -> Optional[Game]:
|
|
63
63
|
"""Returns the game with the specified name."""
|
|
64
64
|
for game in self.games():
|
|
65
65
|
if game.name.casefold() == name.casefold():
|
|
66
66
|
return game
|
|
67
67
|
return None
|
|
68
68
|
|
|
69
|
-
def shortcuts(self) ->
|
|
69
|
+
def shortcuts(self) -> Generator[Shortcut, None, None]:
|
|
70
70
|
"""Returns the Non-Steam shortcuts from the Steam library."""
|
|
71
|
-
shortcuts = []
|
|
72
71
|
for user in self._steam.users:
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
for shortcut in user.shortcuts():
|
|
73
|
+
yield shortcut
|
|
75
74
|
|
|
76
|
-
def all(self) ->
|
|
75
|
+
def all(self) -> Generator[Union[Game, Shortcut], None, None]:
|
|
77
76
|
"""Returns all the games and shortcuts from the Steam library."""
|
|
78
|
-
|
|
77
|
+
for game in self.games():
|
|
78
|
+
yield game
|
|
79
|
+
for shortcut in self.shortcuts():
|
|
80
|
+
yield shortcut
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
from .game import Game
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class LibraryFolder:
|
|
11
|
+
"""Represents a Steam library folder."""
|
|
12
|
+
library_cache_path: Path
|
|
13
|
+
path: str
|
|
14
|
+
apps: List[str]
|
|
15
|
+
|
|
16
|
+
def get_games(self) -> List[Game]:
|
|
17
|
+
return [Game(self.library_cache_path, self.path, appid) for appid in self.apps]
|