Skip to content

Commit

Permalink
Merge pull request #12 from Garulf/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
Garulf authored Nov 26, 2021
2 parents 66fdd26 + 7f0d2a6 commit b693118
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 113 deletions.
2 changes: 1 addition & 1 deletion plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"Name": "Steam Search",
"Description": "Search and launch your Steam Game library",
"Author": "Garulf",
"Version": "1.2.1",
"Version": "2.0.0",
"Language": "python",
"Website": "https://github.com/Garulf/Steam-Search",
"IcoPath": "plugin\\icon\\steam-icon.png",
Expand Down
117 changes: 110 additions & 7 deletions plugin/helper.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,115 @@
from pathlib import Path
import winreg as reg
from winreg import HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE

STEAM_REG_KEY = r"SOFTWARE\WOW6432Node\Valve\Steam"
import vdf

with reg.OpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\WOW6432Node\Valve\Steam") as hkey:
try:
steam_path = reg.QueryValueEx(hkey, "InstallPath")[0]
except FileNotFoundError:
steam_path = None
STEAM_SUB_KEY = r'SOFTWARE\WOW6432Node\Valve\Steam'

STEAM_PATH = steam_path
DEFAULT_STEAM_PATH = r"c:\Program Files (x86)\Steam"
LIBRARY_CACHE_EXT = '.jpg'
ICON_SUFFIX = '_icon'
HEADER_SUFFIX = '_header'
LIBRARY_SUFFIX = '_library_600x900'
LIBRARY_HERO_SUFFIX = '_library_hero'
STEAMAPPS_FOLDER = 'steamapps'


class Steam(object):

def __init__(self, steam_path=None):
self._steam_path = None

@property
def steam_path(self):
if self._steam_path is None:
with reg.OpenKey(HKEY_LOCAL_MACHINE, STEAM_SUB_KEY) as hkey:
try:
self._steam_path = Path(reg.QueryValueEx(hkey, "InstallPath")[0])
except FileNotFoundError:
pass
return self._steam_path

def all_games(self):
games = []
for library in self.libraries():
for game in library.games():
games.append(game)
return games

def libraries(self):
if self.steam_path is None:
return []
libraries = []
libraries_manifest_path = self.steam_path.joinpath('steamapps', 'libraryfolders.vdf')
if not libraries_manifest_path.exists():
return []
try:
library_folders = vdf.load(open(libraries_manifest_path, 'r'))
except FileNotFoundError:
pass
else:
if library_folders.get('libraryfolders'):
libraries_key = 'libraryfolders'
else:
libraries_key = 'LibraryFolders'
for item in library_folders[libraries_key].keys():
if item.isdigit():
try:
library_path = SteamLibrary(self.steam_path, library_folders[libraries_key][item]['path'])
except TypeError:
library_path = SteamLibrary(self.steam_path, library_folders[libraries_key][item])
libraries.append(library_path)
return libraries

class SteamLibrary(object):

def __init__(self, steam_path, library_path):
self._steam_path = steam_path
self._library_path = Path(library_path)

def __str__(self):
return str(self._library_path)

def __repr__(self):
return f'<SteamLibrary {self.__str__()}>'

def games(self):
games = []
for manifest in self._library_path.joinpath(STEAMAPPS_FOLDER).glob('*.acf'):
try:
_game_manifest = vdf.load(open(manifest, 'r'))
game = SteamGame(_game_manifest["AppState"]["appid"], _game_manifest["AppState"]["name"], _game_manifest["AppState"]["installdir"], self._steam_path, self._library_path)
except FileNotFoundError:
pass
except SyntaxError:
continue
else:
games.append(game)
return games

class SteamGame(object):
"""Represents a steam game"""

def __init__(self, id, name, installdir, steam_path, library_path):
self.id = id
self.name = name.replace('â„¢', '')
self.installdir = installdir
self.steam_path = steam_path
self.library_path = library_path
self._appcache_path = Path(self.steam_path).joinpath("appcache", "librarycache")

def icon(self):
return self._appcache_path.joinpath(f"{self.id}{ICON_SUFFIX}{LIBRARY_CACHE_EXT}")

def header(self):
return self._appcache_path.joinpath(f"{self.id}{HEADER_SUFFIX}{LIBRARY_CACHE_EXT}")

def hero(self):
return self._reappcache_path.joinpath(f"{self.id}{LIBRARY_HERO_SUFFIX}{LIBRARY_CACHE_EXT}")

def run_game_url(self):
return f'steam://rumgameid/{self.id}'

def install_path(self):
return self.library_path.joinpath('steamapps', 'common', self.installdir)
145 changes: 41 additions & 104 deletions plugin/steam_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,128 +5,65 @@
import webbrowser
from pathlib import Path

from helper import STEAM_PATH
import vdf
from flox import Flox
from helper import Steam

DEFAULT_DRIVE = "C:"
SYSTEM_DRIVE = os.environ.get("SYSTEMDRIVE", DEFAULT_DRIVE)
STEAM_FOLDER = STEAM_PATH or os.path.join(
f"{SYSTEM_DRIVE}\\", "Program Files (x86)", "Steam"
)
LIBRARIES_CONFIG = os.path.join(STEAM_FOLDER, "config", "libraryfolders.vdf")
LIBRARIES_STEAMAPPS = os.path.join(STEAM_FOLDER, "steamapps", "libraryfolders.vdf")
EXE_FILTER = ["installer", "help", "skse64_loader.exe"]
from flox import Flox


class SteamSearch(Flox):
def __init__(self):
self._steam_folder = None
self._steam_libraries = None
self._library_paths = None
self._steam_folder = STEAM_FOLDER
self.games = []
self._steam = Steam()
super().__init__()

@property
def steam_folder(self):
if self._steamfolder is None:
self._steam_folder = self.settings.get("steam_folder", STEAM_FOLDER)
return self._steam_folder

@property
def library_paths(self):
if self._library_paths is None:
steam_libraries = (
LIBRARIES_CONFIG
if Path(LIBRARIES_CONFIG).exists()
else LIBRARIES_STEAMAPPS
)
library_paths = [self._steam_folder]
try:
library_folders = vdf.load(open(steam_libraries, "r"))
except FileNotFoundError:
pass
else:
if library_folders.get("libraryfolders"):
libraries_key = "libraryfolders"
else:
libraries_key = "LibraryFolders"
for item in library_folders[libraries_key].keys():
if item.isdigit():
try:
library_paths.append(
library_folders[libraries_key][item]["path"]
)
except TypeError:
library_paths.append(library_folders[libraries_key][item])

self._library_paths = library_paths
return self._library_paths

def load_games(self):
for path in self.library_paths:
for manifest in Path(path, "steamapps").glob("*.acf"):
self.add_manifest(manifest, path)

def find_icon(self, install_dir, name):
first_exe = None
game_files = Path(install_dir).glob("**/*.exe")
for file in game_files:
if file.name.lower() not in EXE_FILTER:
if first_exe is None:
first_exe = file
if str(file.name).lower().startswith(name[0].lower()):
return str(file)

return str(first_exe)

def add_manifest(self, file, path):
try:
manifest = vdf.load(open(file))
except (SyntaxError, UnicodeDecodeError):
self.logger.debug(f"Error loading {path}, {file}")
pass
else:
install_dir = Path(path).joinpath(
"steamapps", "common", manifest["AppState"]["installdir"]
)
self.games.append(
{
"id": manifest["AppState"]["appid"],
"name": manifest["AppState"]["name"],
"install_dir": str(install_dir),
}
)

def grab_icon(self, id):
game_icon = "./icon/steam-icon.png"
icon_name = f"{id}_icon.jpg"
icon_path = os.path.join(self._steam_folder, "appcache", "librarycache", icon_name)
if os.path.exists(icon_path):
return icon_path
return game_icon

def query(self, query):
self.load_games()
games = self._steam.all_games()
q = query.lower()
pattern = ".*?".join(q)
regex = re.compile(pattern)
for game in self.games:
match = regex.search(game["name"].lower())
if match:
icon = self.grab_icon(game["id"])
for game in games:
match = regex.search(game.name.lower())
if match:
self.add_item(
title=game["name"],
subtitle=game["install_dir"],
icon=icon,
title=game.name,
subtitle=str(game.install_path()),
icon=str(game.icon()),
method="launch_game",
parameters=[game["id"]],
parameters=[game.id],
context=[game.id]
)

def context_menu(self, data):
game_id = data[0]
self.add_item(
title="Show in Steam store",
subtitle="Opens game's Steam store page",
method="launch_store",
parameters=[game_id]
)
self.add_item(
title="Show News",
subtitle="Opens game's news page in Steam",
method="launch_news",
parameters=[game_id]
)
self.add_item(
title="Uninstall Game",
subtitle="Uninstall this game from your Steam library",
method="uninstall_game",
parameters=[game_id]
)

def launch_game(self, game_id):
webbrowser.open("steam://rungameid/{}".format(game_id))

def launch_store(self, game_id):
webbrowser.open("steam://store/{}".format(game_id))

def uninstall_game(self, game_id):
webbrowser.open("steam://uninstall/{}".format(game_id))

def launch_news(self, game_id):
webbrowser.open("steam://appnews/{}".format(game_id))

if __name__ == "__main__":
SteamSearch()
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
git+git://github.com/garulf/flox@v0.4.0#egg=flox
git+git://github.com/garulf/flox@v0.5.1#egg=flox
vdf==3.4

0 comments on commit b693118

Please sign in to comment.