12 Commits

Author SHA1 Message Date
bsjohnson20 ba18af4a70 change to folder text
Package Application with PyInstaller / build (push) Successful in 44s
2025-07-23 17:42:23 +01:00
bsjohnson20 d977b2b621 add small exception handler for unable to open folder 2025-07-23 17:41:53 +01:00
LunaChocken 26840e7a40 added timing
Package Application with PyInstaller / build (push) Successful in 27s
2025-06-09 18:02:02 +01:00
LunaChocken 153bf640be add caching
Package Application with PyInstaller / build (push) Successful in 26s
2025-06-09 17:26:44 +01:00
LunaChocken 711579e2b1 rename file
Package Application with PyInstaller / build (push) Successful in 27s
2025-06-09 16:10:22 +01:00
LunaChocken a4f0e5eece add caching
Package Application with PyInstaller / build (push) Failing after 45s
2025-06-09 16:07:29 +01:00
LunaChocken d8a49d04a3 change name of executable
Package Application with PyInstaller / build (push) Has been cancelled
2025-06-09 14:53:01 +01:00
LunaChocken 9c2073f1ad fix acf_path having "dir" indicator and not file
Package Application with PyInstaller / build (push) Successful in 27s
2025-06-08 23:02:33 +01:00
LunaChocken 4c39ab90dc Refactor game info display with TableItem class
Package Application with PyInstaller / build (push) Successful in 28s
2025-06-08 20:34:36 +01:00
LunaChocken 10e4b73b00 add copy of gitea workflow to github folder workflows
Package Application with PyInstaller / build (push) Successful in 27s
2025-06-08 20:04:39 +01:00
LunaChocken e3c35285f2 - Add all command to display All games
Package Application with PyInstaller / build (push) Successful in 26s
- Remove printing all games immediately
2025-06-08 19:30:10 +01:00
LunaChocken 02731ad9ca Disable release builder. Doesnt work
Package Application with PyInstaller / build (push) Successful in 31s
Add ctrl+C better support using try except statements
Add workshop paths
2025-06-07 23:52:05 +01:00
11 changed files with 310 additions and 96 deletions
+4 -6
View File
@@ -7,14 +7,13 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v4
with: with:
python-version: '3.11.0' python-version: "3.11.0"
- name: Install dependencies - name: Install dependencies
run: > run: >
@@ -24,11 +23,10 @@ jobs:
- name: Package Application with PyInstaller - name: Package Application with PyInstaller
run: | run: |
pyinstaller steamPathCLI.spec pyinstaller spath.spec
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: steamPathCLI.sh name: spath.sh
path: dist/steamPathCLI.sh path: dist/spath.sh
+7 -5
View File
@@ -1,7 +1,9 @@
# DISABLED
name: release name: release
on: # on:
workflow_dispatch: # workflow_dispatch:
jobs: jobs:
release: release:
@@ -16,7 +18,7 @@ jobs:
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v4
with: with:
python-version: '3.11.0' python-version: "3.11.0"
- name: Install dependencies - name: Install dependencies
run: > run: >
python -m pip install --upgrade pip python -m pip install --upgrade pip
@@ -25,9 +27,9 @@ jobs:
- name: Package Application with PyInstaller - name: Package Application with PyInstaller
run: | run: |
pyinstaller steamPathCLI.spec pyinstaller spath.spec
uses: https://gitea.com/actions/release-action@main uses: https://gitea.com/actions/release-action@main
with: with:
files: |- files: |-
dist/** dist/**
api_key: '${{secrets.RELEASE_TOKEN}}' api_key: "${{secrets.RELEASE_TOKEN}}"
+32
View File
@@ -0,0 +1,32 @@
name: Package Application with PyInstaller
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.11.0"
- name: Install dependencies
run: >
python -m pip install --upgrade pip
pip install . # Install dependencies; modify if using requirements.txt
- name: Package Application with PyInstaller
run: |
pyinstaller spath.spec
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: spath.sh
path: dist/spath.sh
+116 -22
View File
@@ -6,47 +6,121 @@ from rich.columns import Columns
from rich.panel import Panel from rich.panel import Panel
import src.vdfparser as vd import src.vdfparser as vd
from src.prompt_helper import PromptHelper from src.prompt_helper import PromptHelper
# Commandline options support
import argparse
# basic attempt at making program faster
import datetime
from src.timing import timeit
from cachier import cachier
cachier = cachier(stale_after=datetime.timedelta(days=1), cache_dir='/tmp/.cache')
class TableItem:
def __init__(self, name, data='', colour='white', type='desc'):
self.name = name
self.data = data
self.type = type
self.colour = colour
def generate_link(self):
if self.type == 'file':
return f"[{self.colour}][link=file://{self.data}]file[/link][/{self.colour}]"
elif self.type == 'dir':
return f"[{self.colour}][link=file://{self.data}]dir[/link][/{self.colour}]"
else:
return f"[{self.colour}]{self.data}[/{self.colour}]"
def __str__(self):
return f"\n[white]{self.name}[/white]: [{self.colour}]"+self.generate_link()+""
class SteamGamePathTool: class SteamGamePathTool:
def __init__(self): # arg mode means user input will be skipped and take all args from command line
self.steam_path = vd.fetch_steam_path() def __init__(self, steamPath=vd.fetch_steam_path(), arg_mode_enabled=None, game_name=None, game_id=None, all=False):
self.steam_path = steamPath
self.steam_vdf_path = vd.fetch_steam_vdf() self.steam_vdf_path = vd.fetch_steam_vdf()
# arg support
self.arg_mode_enabled = arg_mode_enabled
self.game_name = game_name
self.game_id = game_id
self.all = all
if self.steam_vdf_path is None: if self.steam_vdf_path is None:
print("Steam VDF file not found") print("Steam VDF file not found")
# Add user input to attempt to find # Add user input to attempt to find
self.steam_vdf_path = prompt("Enter the path to the Steam VDF file: ", default=self.steam_path) self.steam_vdf_path = prompt("Enter the path to the Steam VDF file: ", default=self.steam_path)
# Fetch steam's vdf file and convert to json dict
self.steam_vdf = vd.parse_vdf(self.steam_vdf_path) self.steam_vdf = vd.parse_vdf(self.steam_vdf_path)
# Possible other locations where steamlibrary could be found
self.steam_library_locations = vd.find_extra_locations(self.steam_vdf) self.steam_library_locations = vd.find_extra_locations(self.steam_vdf)
games = vd.fetchall_vdfs(self.steam_vdf) games = vd.fetchall_vdfs(self.steam_vdf)
games = self.sort_games(games) self.games = self.sort_games(games)
console = Console()
game_rend = [Panel(self.get_game_content(game), expand=True) for game in games]
console.print(Columns(game_rend))
self.prompter = PromptHelper(games)
for library in self.steam_library_locations: for library in self.steam_library_locations:
print(f"[+] Found Steam library at: {library}") print(f"[+] Found Steam library at: {library}")
self.prompter = PromptHelper(games) if arg_mode_enabled:
self.arg_mode()
return
while True: while True:
self.prompt_user() self.prompt_user()
input("Press Enter to continue...") try:
input("Press Enter to continue...")
except KeyboardInterrupt:
print("\nExiting...")
break
def prompt_user(self): def arg_mode(self):
game = self.prompter.prompt_game(text="Input (game name | appid | q/quit): ") if self.all:
if game is None: console = Console()
game_rend = [Panel(self.get_game_content(game), expand=True) for game in self.games]
console.print(Columns(game_rend))
return
if self.game_id:
game = self.prompter.find_game_num(self.game_id)
elif self.game_name:
game = self.prompter.find_game_str(self.game_name)
else:
return return
console = Console() console = Console()
console.print(Panel(self.get_game_content(game), expand=True)) data = self.get_game_content(game)
if data:
console.print(Panel(data, expand=True))
else:
console.print("No game found")
@timeit
@cachier
def prepare_all_games(self):
game_rend = [Panel(self.get_game_content(game), expand=True) for game in self.games]
return game_rend
def print_all_games(self):
game_rend = self.prepare_all_games()
console = Console()
console.print(Columns(game_rend))
def prompt_user(self):
game = self.prompter.prompt_game(text="Input (game name | appid | all | q/quit): ")
if game is None:
return
elif game == 'fetch_all_games':
self.print_all_games()
else:
console = Console()
console.print(Panel(self.get_game_content(game), expand=True))
def sort_games(self, games): def sort_games(self, games):
return sorted(games, key=lambda x: x['name']) return sorted(games, key=lambda x: x['name'])
@cachier
def get_game_content(self, game): def get_game_content(self, game):
""" """
Takes a game dictionary and returns a string that formats game information into a string renderable by the console in the rich library. Takes a game dictionary and returns a string that formats game information into a string renderable by the console in the rich library.
@@ -55,14 +129,34 @@ class SteamGamePathTool:
:return: string formatted for table using rich library :return: string formatted for table using rich library
""" """
# Spaces must be replaced with %20 otherwise they won't link properly # Spaces must be replaced with %20 otherwise they won't link properly
try:
string = f"""[b]{game['name']}[/b] string = f"""[b]{game['name']}[/b]"""
[white]Game ID: [yellow]{game['appid']} string+=str(TableItem(name="Game ID", data=f"{game['appid']}", colour="blue",type="desc"))
[white]Game Size: [red]{int(game['SizeOnDisk'])/(1024*1024)/1024:.2f} GB[/red] string+=str(TableItem(name="Game acf", data=f"{game['acf_path'].replace(' ', '%20')}", colour="green",type="file"))
[white]Game acf: [green][link=file://{game['acf_path'].replace(' ', '%20')}]file[/link][/green] string+=str(TableItem(name="Game Path", data=f"{game['true_path'].replace(' ', '%20')}", colour="blue",type="dir"))
[white]Game Path: [green][link=file://{game['true_path'].replace(' ', '%20')}]dir[/link][/green]""" if game['workshop_path']:
if game['compatdata_path']: string+=str(TableItem(name="Game Workshop Path", data=f"{game['workshop_path'].replace(' ', '%20')}", colour="blue", type="dir"))
string += f"\n\t[white]Compatdata dir: [blue][link=file://{game['compatdata_path'].replace(' ', '%20')}]dir[/link][/blue]" if game["compatdata_path"]:
string+=str(TableItem(name="Game Compatdata Path",data=f"{game['compatdata_path'].replace(' ', '%20')}",colour="blue",type="dir"))
except TypeError:
return None
return string return string
if __name__ == "__main__": if __name__ == "__main__":
steam_path_tool = SteamGamePathTool() print("""Welcome to S-Path
A tool to print the locations of steam games, their respective gameID, workshop path, etc.""")
parser = argparse.ArgumentParser()
parser.add_argument('--name', help='Game name to search for')
parser.add_argument('--id', help='Game ID to search for')
# parser.add_argument('--help', help='Display help message')
parser.add_argument('--steam-path', help='Steam path to use')
parser.add_argument('--all', help='Displays all games installed', action='store_true')
args = parser.parse_args()
if args.name or args.id or args.all:
steam_path_tool = SteamGamePathTool(game_id=args.id, game_name=args.name, arg_mode_enabled=True, all=args.all)
else:
steam_path_tool = SteamGamePathTool()
+1
View File
@@ -5,6 +5,7 @@ description = "Add your description here"
readme = "README.md" readme = "README.md"
requires-python = ">=3.11" requires-python = ">=3.11"
dependencies = [ dependencies = [
"cachier>=3.1.2",
"prompt-toolkit>=3.0.51", "prompt-toolkit>=3.0.51",
"pyinstaller>=6.14.0", "pyinstaller>=6.14.0",
"rich>=14.0.0", "rich>=14.0.0",
+5 -2
View File
@@ -1,11 +1,14 @@
# -*- mode: python ; coding: utf-8 -*- # -*- mode: python ; coding: utf-8 -*-
from PyInstaller.utils.hooks import collect_data_files
datas = []
datas += collect_data_files('cachier')
a = Analysis( a = Analysis(
['main.py'], ['main.py'],
pathex=[], pathex=[],
binaries=[], binaries=[],
datas=[], datas=datas,
hiddenimports=[], hiddenimports=[],
hookspath=[], hookspath=[],
hooksconfig={}, hooksconfig={},
@@ -22,7 +25,7 @@ exe = EXE(
a.binaries, a.binaries,
a.datas, a.datas,
[], [],
name='main', name='spath.sh',
debug=False, debug=False,
bootloader_ignore_signals=False, bootloader_ignore_signals=False,
strip=False, strip=False,
+34 -11
View File
@@ -1,11 +1,19 @@
import prompt_toolkit as pt import prompt_toolkit as pt
from prompt_toolkit.completion import WordCompleter, FuzzyCompleter from prompt_toolkit.completion import WordCompleter, FuzzyCompleter
import sys import sys
import datetime
from src.timing import timeit
from cachier import cachier
cachier = cachier(stale_after=datetime.timedelta(days=1), cache_dir='/tmp/.cache')
@timeit
@cachier
def generate_completer(game_list): def generate_completer(game_list):
g_list = [x['name'] for x in game_list] + [x['appid'] for x in game_list] g_list = [x['name'] for x in game_list] + [x['appid'] for x in game_list]
g_list.append("q") g_list.append("q")
g_list.append("quit") g_list.append("quit")
g_list.append("all")
return FuzzyCompleter(WordCompleter(g_list)) return FuzzyCompleter(WordCompleter(g_list))
class PromptHelper: class PromptHelper:
@@ -13,11 +21,16 @@ class PromptHelper:
self.game_list = game_list self.game_list = game_list
self.completer = generate_completer(game_list) self.completer = generate_completer(game_list)
def find_game_str(self, game_name): @timeit
@cachier
def find_game_str(self, game_name) -> dict|None:
for game in self.game_list: for game in self.game_list:
if game['name'] == game_name: if game['name'] == game_name:
return game return game
def find_game_num(self, appid: str):
@timeit
@cachier
def find_game_num(self, appid: str) -> dict|None:
for game in self.game_list: for game in self.game_list:
if game['appid'] == appid: if game['appid'] == appid:
return game return game
@@ -25,13 +38,23 @@ class PromptHelper:
return None return None
def prompt_game(self, text, default="None"): def prompt_game(self, text, default="None"):
response = pt.prompt(text, completer=self.completer, complete_while_typing=True) try:
if response == "": response = pt.prompt(text, completer=self.completer, complete_while_typing=True)
return None except KeyboardInterrupt:
elif response == "q" or response == "quit": print("\nCtrl+C received. Exiting.")
print("Goodbye!")
sys.exit(0) sys.exit(0)
elif response[0].isalpha(): try:
return self.find_game_str(response) if response == "":
else: return None
return self.find_game_num(response) elif response == "all":
return "fetch_all_games"
elif response == "q" or response == "quit":
print("Goodbye!")
sys.exit(0)
elif response[0].isalpha():
return self.find_game_str(response)
else:
return self.find_game_num(response)
except ValueError:
print("Invalid input")
return None
+16
View File
@@ -0,0 +1,16 @@
from functools import wraps
import time
debug = True
def timeit(func):
@wraps(func)
def timeit_wrapper(*args, **kwargs):
start_time = time.perf_counter()
result = func(*args, **kwargs)
end_time = time.perf_counter()
total_time = end_time - start_time
if debug:
print(f'Function {func.__name__} Took {total_time:.4f} seconds.')
return result
return timeit_wrapper
+25 -12
View File
@@ -1,6 +1,12 @@
import os import os
import vdf import vdf
from cachier import cachier
import datetime
from src.timing import timeit
cachier = cachier(stale_after=datetime.timedelta(days=1), cache_dir='/tmp/.cache')
@cachier
@timeit
def parse_vdf(steam_vdf_path) -> dict: def parse_vdf(steam_vdf_path) -> dict:
""" """
Reads the Steam vdf file at the given path and returns the corresponding dict. Reads the Steam vdf file at the given path and returns the corresponding dict.
@@ -11,6 +17,7 @@ def parse_vdf(steam_vdf_path) -> dict:
vdf_data = vdf.loads(open(steam_vdf_path, 'r').read()) vdf_data = vdf.loads(open(steam_vdf_path, 'r').read())
return vdf_data return vdf_data
@cachier
def fetch_ids(vdf_json: dict): def fetch_ids(vdf_json: dict):
""" """
Prints all Steam app IDs from the given vdf data. Prints all Steam app IDs from the given vdf data.
@@ -27,8 +34,8 @@ def fetch_steam_path() -> str:
""" """
Determines the Steam installation path by checking common locations. Determines the Steam installation path by checking common locations.
Returns the path to the Steam 'steamapps' directory. Returns the path to the Steam 'steamapps' directory.
Prefers the path used by the apt installation if it exists. Prefers the path used by the apt installation if it exists.
Otherwise, returns the path used by the Flatpak installation. Otherwise, returns the path used by the Flatpak installation.
:return: Path to the Steam 'steamapps' directory :return: Path to the Steam 'steamapps' directory
@@ -76,11 +83,13 @@ def find_extra_locations(vdf_json):
return steam_library_locations return steam_library_locations
# Reads manifest of individual game vdfs # Reads manifest of individual game vdfs
@cachier
def read_game_vdf(gameID: int, steam_vdf_json: dict, path, game) -> dict: def read_game_vdf(gameID: int, steam_vdf_json: dict, path, game) -> dict:
with open(os.path.join(path, game), 'r') as f: with open(os.path.join(path, game), 'r') as f:
game_vdf = vdf.loads(f.read()) game_vdf = vdf.loads(f.read())
return game_vdf['AppState'] return game_vdf['AppState']
@cachier
def fetchall_vdfs(steam_vdf_json: dict): def fetchall_vdfs(steam_vdf_json: dict):
""" """
Reads all Steam game manifests in the given Steam VDF data and returns a list of dictionaries containing information about each game. Reads all Steam game manifests in the given Steam VDF data and returns a list of dictionaries containing information about each game.
@@ -92,14 +101,18 @@ def fetchall_vdfs(steam_vdf_json: dict):
for library in steam_vdf_json["libraryfolders"]: for library in steam_vdf_json["libraryfolders"]:
path = steam_vdf_json['libraryfolders'][library]['path'] path = steam_vdf_json['libraryfolders'][library]['path']
steamapps = os.path.join(path, "steamapps") steamapps = os.path.join(path, "steamapps")
for game in os.listdir(steamapps): try:
if game.endswith(".acf"): for game in os.listdir(steamapps):
gameID = int(game.split('.')[0].split('_')[1]) if game.endswith(".acf"):
parsed_game = read_game_vdf(gameID, steam_vdf_json, steamapps, game) gameID = int(game.split('.')[0].split('_')[1])
parsed_game['acf_path'] = os.path.join(steamapps, game) parsed_game = read_game_vdf(gameID, steam_vdf_json, steamapps, game)
parsed_game['root_steam_folder'] = path parsed_game['acf_path'] = os.path.join(steamapps, game)
parsed_game['true_path'] = os.path.join(steamapps, "common", parsed_game['installdir']) parsed_game['root_steam_folder'] = path
parsed_game['compatdata_path'] = os.path.join(steamapps, "compatdata", str(gameID)) if os.path.exists(os.path.join(steamapps, "compatdata", str(gameID))) else None parsed_game['true_path'] = os.path.join(steamapps, "common", parsed_game['installdir'])
games.append(parsed_game) parsed_game['compatdata_path'] = os.path.join(steamapps, "compatdata", str(gameID)) if os.path.exists(os.path.join(steamapps, "compatdata", str(gameID))) else None
# print("Game name:", parsed_game['name'], "ID:", gameID, "Path:", parsed_game['true_path']) parsed_game['workshop_path'] = os.path.join(steamapps, "workshop", "content", str(gameID)) if os.path.exists(os.path.join(steamapps, "workshop", "content", str(gameID))) else ""
games.append(parsed_game)
# print("Game name:", parsed_game['name'], "ID:", gameID, "Path:", parsed_game['true_path'])
except Exception as e:
print(f"Error processing folder {steamapps}: {e}")
return games return games
-38
View File
@@ -1,38 +0,0 @@
# -*- mode: python ; coding: utf-8 -*-
a = Analysis(
['main.py'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='steamPathCLI.sh',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
Generated
+70
View File
@@ -11,6 +11,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/4d/3f/3bc3f1d83f6e4a7fcb834d3720544ca597590425be5ba9db032b2bf322a2/altgraph-0.17.4-py2.py3-none-any.whl", hash = "sha256:642743b4750de17e655e6711601b077bc6598dbfa3ba5fa2b2a35ce12b508dff", size = 21212 }, { url = "https://files.pythonhosted.org/packages/4d/3f/3bc3f1d83f6e4a7fcb834d3720544ca597590425be5ba9db032b2bf322a2/altgraph-0.17.4-py2.py3-none-any.whl", hash = "sha256:642743b4750de17e655e6711601b077bc6598dbfa3ba5fa2b2a35ce12b508dff", size = 21212 },
] ]
[[package]]
name = "cachier"
version = "3.1.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "portalocker" },
{ name = "watchdog" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d8/95/f17aaba6b786f968a8078c8e86f8004652ba9157d8aeb329837c85d10a43/cachier-3.1.2.tar.gz", hash = "sha256:8ef53b6ae83ba04d8864d2e1f45eb8b46eecb611d0f6a24c006e8fda074c6557", size = 27319 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d7/9f/11e07e8ffbc921d7c963568937b0290ac9204455a3f3cd866164e82f36ba/cachier-3.1.2-py3-none-any.whl", hash = "sha256:24c0fefd6aef1d38a4337590aba82f3c4bd844df18393ba93c7fd7dcc98da304", size = 24098 },
]
[[package]] [[package]]
name = "macholib" name = "macholib"
version = "1.16.3" version = "1.16.3"
@@ -62,6 +75,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/55/26/d0ad8b448476d0a1e8d3ea5622dc77b916db84c6aa3cb1e1c0965af948fc/pefile-2023.2.7-py3-none-any.whl", hash = "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6", size = 71791 }, { url = "https://files.pythonhosted.org/packages/55/26/d0ad8b448476d0a1e8d3ea5622dc77b916db84c6aa3cb1e1c0965af948fc/pefile-2023.2.7-py3-none-any.whl", hash = "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6", size = 71791 },
] ]
[[package]]
name = "portalocker"
version = "3.1.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pywin32", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ac/91/8bfe23e1f7f630f2061ef38b5225d9fda9068d6a30fcbc187951e678e630/portalocker-3.1.1.tar.gz", hash = "sha256:ec20f6dda2ad9ce89fa399a5f31f4f1495f515958f0cb7ca6543cef7bb5a749e", size = 43708 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f7/60/1974cfdd5bb770568ddc6f89f3e0df4cfdd1acffd5a609dff5e95f48c6e2/portalocker-3.1.1-py3-none-any.whl", hash = "sha256:80e984e24de292ff258a5bea0e4f3f778fff84c0ae1275dbaebc4658de4aacb3", size = 19661 },
]
[[package]] [[package]]
name = "prompt-toolkit" name = "prompt-toolkit"
version = "3.0.51" version = "3.0.51"
@@ -124,6 +149,22 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d3/e1/ed48c7074145898e5c5b0072e87be975c5bd6a1d0f08c27a1daa7064fca0/pyinstaller_hooks_contrib-2025.4-py3-none-any.whl", hash = "sha256:6c2d73269b4c484eb40051fc1acee0beb113c2cfb3b37437b8394faae6f0d072", size = 434451 }, { url = "https://files.pythonhosted.org/packages/d3/e1/ed48c7074145898e5c5b0072e87be975c5bd6a1d0f08c27a1daa7064fca0/pyinstaller_hooks_contrib-2025.4-py3-none-any.whl", hash = "sha256:6c2d73269b4c484eb40051fc1acee0beb113c2cfb3b37437b8394faae6f0d072", size = 434451 },
] ]
[[package]]
name = "pywin32"
version = "310"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f7/b1/68aa2986129fb1011dabbe95f0136f44509afaf072b12b8f815905a39f33/pywin32-310-cp311-cp311-win32.whl", hash = "sha256:1e765f9564e83011a63321bb9d27ec456a0ed90d3732c4b2e312b855365ed8bd", size = 8784284 },
{ url = "https://files.pythonhosted.org/packages/b3/bd/d1592635992dd8db5bb8ace0551bc3a769de1ac8850200cfa517e72739fb/pywin32-310-cp311-cp311-win_amd64.whl", hash = "sha256:126298077a9d7c95c53823934f000599f66ec9296b09167810eb24875f32689c", size = 9520748 },
{ url = "https://files.pythonhosted.org/packages/90/b1/ac8b1ffce6603849eb45a91cf126c0fa5431f186c2e768bf56889c46f51c/pywin32-310-cp311-cp311-win_arm64.whl", hash = "sha256:19ec5fc9b1d51c4350be7bb00760ffce46e6c95eaf2f0b2f1150657b1a43c582", size = 8455941 },
{ url = "https://files.pythonhosted.org/packages/6b/ec/4fdbe47932f671d6e348474ea35ed94227fb5df56a7c30cbbb42cd396ed0/pywin32-310-cp312-cp312-win32.whl", hash = "sha256:8a75a5cc3893e83a108c05d82198880704c44bbaee4d06e442e471d3c9ea4f3d", size = 8796239 },
{ url = "https://files.pythonhosted.org/packages/e3/e5/b0627f8bb84e06991bea89ad8153a9e50ace40b2e1195d68e9dff6b03d0f/pywin32-310-cp312-cp312-win_amd64.whl", hash = "sha256:bf5c397c9a9a19a6f62f3fb821fbf36cac08f03770056711f765ec1503972060", size = 9503839 },
{ url = "https://files.pythonhosted.org/packages/1f/32/9ccf53748df72301a89713936645a664ec001abd35ecc8578beda593d37d/pywin32-310-cp312-cp312-win_arm64.whl", hash = "sha256:2349cc906eae872d0663d4d6290d13b90621eaf78964bb1578632ff20e152966", size = 8459470 },
{ url = "https://files.pythonhosted.org/packages/1c/09/9c1b978ffc4ae53999e89c19c77ba882d9fce476729f23ef55211ea1c034/pywin32-310-cp313-cp313-win32.whl", hash = "sha256:5d241a659c496ada3253cd01cfaa779b048e90ce4b2b38cd44168ad555ce74ab", size = 8794384 },
{ url = "https://files.pythonhosted.org/packages/45/3c/b4640f740ffebadd5d34df35fecba0e1cfef8fde9f3e594df91c28ad9b50/pywin32-310-cp313-cp313-win_amd64.whl", hash = "sha256:667827eb3a90208ddbdcc9e860c81bde63a135710e21e4cb3348968e4bd5249e", size = 9503039 },
{ url = "https://files.pythonhosted.org/packages/b4/f4/f785020090fb050e7fb6d34b780f2231f302609dc964672f72bfaeb59a28/pywin32-310-cp313-cp313-win_arm64.whl", hash = "sha256:e308f831de771482b7cf692a1f308f8fca701b2d8f9dde6cc440c7da17e47b33", size = 8458152 },
]
[[package]] [[package]]
name = "pywin32-ctypes" name = "pywin32-ctypes"
version = "0.2.3" version = "0.2.3"
@@ -160,6 +201,7 @@ name = "steamgamepath"
version = "0.1.0" version = "0.1.0"
source = { virtual = "." } source = { virtual = "." }
dependencies = [ dependencies = [
{ name = "cachier" },
{ name = "prompt-toolkit" }, { name = "prompt-toolkit" },
{ name = "pyinstaller" }, { name = "pyinstaller" },
{ name = "rich" }, { name = "rich" },
@@ -168,6 +210,7 @@ dependencies = [
[package.metadata] [package.metadata]
requires-dist = [ requires-dist = [
{ name = "cachier", specifier = ">=3.1.2" },
{ name = "prompt-toolkit", specifier = ">=3.0.51" }, { name = "prompt-toolkit", specifier = ">=3.0.51" },
{ name = "pyinstaller", specifier = ">=6.14.0" }, { name = "pyinstaller", specifier = ">=6.14.0" },
{ name = "rich", specifier = ">=14.0.0" }, { name = "rich", specifier = ">=14.0.0" },
@@ -183,6 +226,33 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/96/60/6456b687cf55cf60020dcd01f9bc51561c3cc84f05fd8e0feb71ce60f894/vdf-3.4-py2.py3-none-any.whl", hash = "sha256:68c1a125cc49e343d535af2dd25074e9cb0908c6607f073947c4a04bbe234534", size = 10357 }, { url = "https://files.pythonhosted.org/packages/96/60/6456b687cf55cf60020dcd01f9bc51561c3cc84f05fd8e0feb71ce60f894/vdf-3.4-py2.py3-none-any.whl", hash = "sha256:68c1a125cc49e343d535af2dd25074e9cb0908c6607f073947c4a04bbe234534", size = 10357 },
] ]
[[package]]
name = "watchdog"
version = "6.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e0/24/d9be5cd6642a6aa68352ded4b4b10fb0d7889cb7f45814fb92cecd35f101/watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c", size = 96393 },
{ url = "https://files.pythonhosted.org/packages/63/7a/6013b0d8dbc56adca7fdd4f0beed381c59f6752341b12fa0886fa7afc78b/watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2", size = 88392 },
{ url = "https://files.pythonhosted.org/packages/d1/40/b75381494851556de56281e053700e46bff5b37bf4c7267e858640af5a7f/watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c", size = 89019 },
{ url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471 },
{ url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449 },
{ url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054 },
{ url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480 },
{ url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451 },
{ url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057 },
{ url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079 },
{ url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078 },
{ url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076 },
{ url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077 },
{ url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078 },
{ url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077 },
{ url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078 },
{ url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065 },
{ url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070 },
{ url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067 },
]
[[package]] [[package]]
name = "wcwidth" name = "wcwidth"
version = "0.2.13" version = "0.2.13"