Create loadSteamInfo
This commit is contained in:
498
loadSteamInfo
Normal file
498
loadSteamInfo
Normal file
@@ -0,0 +1,498 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
from steamctl.clients import CachingSteamClient
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
from pathlib import Path
|
||||||
|
from argparse import Namespace
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
|
steamClient = False
|
||||||
|
|
||||||
|
class App:
|
||||||
|
appId = 0
|
||||||
|
name = ""
|
||||||
|
executable = ""
|
||||||
|
start = ""
|
||||||
|
icon = ""
|
||||||
|
shortcut = ""
|
||||||
|
options = ""
|
||||||
|
hidden = False
|
||||||
|
desktopConfig = False
|
||||||
|
overlay = False
|
||||||
|
vr = False
|
||||||
|
devkit = False
|
||||||
|
devkitId = ""
|
||||||
|
playTime = 0
|
||||||
|
tags = "null"
|
||||||
|
|
||||||
|
def __init__(self, appId):
|
||||||
|
self.appId = appId
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "App %s (%s at %s)" % (self.appId, self.name, self.executable)
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return self.appId
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if self.appId != other.appId:
|
||||||
|
return False
|
||||||
|
if self.name != other.name:
|
||||||
|
return False
|
||||||
|
if self.executable != other.executable:
|
||||||
|
return False
|
||||||
|
if self.start != other.start:
|
||||||
|
return False
|
||||||
|
if self.icon != other.icon:
|
||||||
|
return False
|
||||||
|
if self.shortcut != other.shortcut:
|
||||||
|
return False
|
||||||
|
if self.options != other.options:
|
||||||
|
return False
|
||||||
|
if self.hidden != other.hidden:
|
||||||
|
return False
|
||||||
|
if self.desktopConfig != other.desktopConfig:
|
||||||
|
return False
|
||||||
|
if self.overlay != other.overlay:
|
||||||
|
return False
|
||||||
|
if self.vr != other.vr:
|
||||||
|
return False
|
||||||
|
if self.devkit != other.devkit:
|
||||||
|
return False
|
||||||
|
if self.devkitId != other.devkitId:
|
||||||
|
return False
|
||||||
|
if self.playTime != other.playTime:
|
||||||
|
return False
|
||||||
|
if self.tags != other.tags:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def parseShortcuts(shortcutFile):
|
||||||
|
data = []
|
||||||
|
with open(shortcutFile, 'rb') as shortcutData:
|
||||||
|
rawData = shortcutData.read()
|
||||||
|
i = 0
|
||||||
|
buffer = bytearray()
|
||||||
|
app = None
|
||||||
|
while i < len(rawData):
|
||||||
|
buffer.append(rawData[i])
|
||||||
|
try:
|
||||||
|
buffCont = buffer.decode()
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
#print(rawData[:i])
|
||||||
|
#print("Clearing buffer %s after %d" % (buffer[:-1].decode(), buffer[-1]))
|
||||||
|
buffer.clear()
|
||||||
|
buffCont = ""
|
||||||
|
found = True
|
||||||
|
if buffCont.endswith("shortcuts"):
|
||||||
|
buffer.clear()
|
||||||
|
i += 2
|
||||||
|
while rawData[i] != 2:
|
||||||
|
buffer.append(rawData[i])
|
||||||
|
i += 1
|
||||||
|
data.append({
|
||||||
|
"shortcuts": buffer[:-1].decode()
|
||||||
|
})
|
||||||
|
if buffCont.endswith("appid"):
|
||||||
|
buffer.clear()
|
||||||
|
i += 2
|
||||||
|
# Some app names do have a 1 for one of their bits
|
||||||
|
while rawData[i + 1:i + 8] != bytearray("AppName", "utf-8"):
|
||||||
|
buffer.append(rawData[i])
|
||||||
|
i += 1
|
||||||
|
app = App(int.from_bytes(buffer, "little"))
|
||||||
|
data.append(app)
|
||||||
|
elif buffCont.endswith("AppName"):
|
||||||
|
buffer.clear()
|
||||||
|
i += 2
|
||||||
|
while rawData[i + 1:i + 4] != bytearray("Exe", "utf-8"):
|
||||||
|
buffer.append(rawData[i])
|
||||||
|
i += 1
|
||||||
|
app.name = buffer[:-1].decode()
|
||||||
|
elif buffCont.endswith("Exe"):
|
||||||
|
buffer.clear()
|
||||||
|
i += 2
|
||||||
|
while rawData[i + 1:i + 9] != bytearray("StartDir", "utf-8"):
|
||||||
|
buffer.append(rawData[i])
|
||||||
|
i += 1
|
||||||
|
app.executable = buffer[:-1].decode()
|
||||||
|
elif buffCont.endswith("StartDir"):
|
||||||
|
buffer.clear()
|
||||||
|
i += 2
|
||||||
|
while rawData[i] != 0:
|
||||||
|
buffer.append(rawData[i])
|
||||||
|
i += 1
|
||||||
|
app.start = buffer.decode()
|
||||||
|
elif buffCont.endswith("icon"):
|
||||||
|
buffer.clear()
|
||||||
|
i += 2
|
||||||
|
while rawData[i + 1:i + 13] != bytearray("ShortcutPath", "utf-8"):
|
||||||
|
buffer.append(rawData[i])
|
||||||
|
i += 1
|
||||||
|
app.icon = buffer[:-1].decode()
|
||||||
|
elif buffCont.endswith("ShortcutPath"):
|
||||||
|
buffer.clear()
|
||||||
|
i += 2
|
||||||
|
while rawData[i + 1:i + 14] != bytearray("LaunchOptions", "utf-8"):
|
||||||
|
buffer.append(rawData[i])
|
||||||
|
i += 1
|
||||||
|
app.shortcut = buffer[:-1].decode()
|
||||||
|
elif buffCont.endswith("LaunchOptions"):
|
||||||
|
buffer.clear()
|
||||||
|
i += 2
|
||||||
|
while rawData[i + 1:i + 9] != bytearray("IsHidden", "utf-8"):
|
||||||
|
buffer.append(rawData[i])
|
||||||
|
i += 1
|
||||||
|
app.options = buffer[:-1].decode()
|
||||||
|
elif buffCont.endswith("IsHidden"):
|
||||||
|
buffer.clear()
|
||||||
|
i += 2
|
||||||
|
while len(buffer) < 3:
|
||||||
|
buffer.append(rawData[i])
|
||||||
|
i += 1
|
||||||
|
app.hidden = int.from_bytes(buffer, "little")
|
||||||
|
elif buffCont.endswith("AllowDesktopConfig"):
|
||||||
|
buffer.clear()
|
||||||
|
i += 2
|
||||||
|
while len(buffer) < 3:
|
||||||
|
buffer.append(rawData[i])
|
||||||
|
i += 1
|
||||||
|
app.desktopConfig = int.from_bytes(buffer, "little")
|
||||||
|
elif buffCont.endswith("AllowOverlay"):
|
||||||
|
buffer.clear()
|
||||||
|
i += 2
|
||||||
|
while len(buffer) < 3:
|
||||||
|
buffer.append(rawData[i])
|
||||||
|
i += 1
|
||||||
|
app.overlay = int.from_bytes(buffer, "little")
|
||||||
|
elif buffCont.endswith("OpenVR"):
|
||||||
|
buffer.clear()
|
||||||
|
i += 2
|
||||||
|
while len(buffer) < 3:
|
||||||
|
buffer.append(rawData[i])
|
||||||
|
i += 1
|
||||||
|
app.vr = int.from_bytes(buffer, "little")
|
||||||
|
elif buffCont.endswith("Devkit") and type(app.devkit) == bool:
|
||||||
|
buffer.clear()
|
||||||
|
i += 2
|
||||||
|
while len(buffer) < 3:
|
||||||
|
buffer.append(rawData[i])
|
||||||
|
i += 1
|
||||||
|
app.devkit = int.from_bytes(buffer, "little")
|
||||||
|
elif buffCont.endswith("DevkitGameID"):
|
||||||
|
buffer.clear()
|
||||||
|
i += 2
|
||||||
|
while rawData[i + 1:i + 13] != bytearray("LastPlayTime", "utf-8"):
|
||||||
|
buffer.append(rawData[i])
|
||||||
|
i += 1
|
||||||
|
app.devkitId = buffer[:-1].decode()
|
||||||
|
elif buffCont.endswith("LastPlayTime"):
|
||||||
|
buffer.clear()
|
||||||
|
i += 2
|
||||||
|
while len(buffer) < 4:
|
||||||
|
buffer.append(rawData[i])
|
||||||
|
i += 1
|
||||||
|
app.playTime = int.from_bytes(buffer, "little")
|
||||||
|
elif buffCont.endswith("tags"):
|
||||||
|
buffer.clear()
|
||||||
|
i += 2
|
||||||
|
while i < len(rawData) and rawData[i] != 2:
|
||||||
|
buffer.append(rawData[i])
|
||||||
|
i += 1
|
||||||
|
if i < len(rawData) and rawData[i] == 2:
|
||||||
|
buffer = buffer[:-1]
|
||||||
|
app.tags = buffer.decode()
|
||||||
|
else:
|
||||||
|
found = False
|
||||||
|
#print('\r' + buffCont, end='')
|
||||||
|
if found:
|
||||||
|
#try:
|
||||||
|
#print(": %s" % buffer.decode())
|
||||||
|
#except UnicodeDecodeError:
|
||||||
|
#print(": %s" % int.from_bytes(buffer, "little"))
|
||||||
|
buffer.clear()
|
||||||
|
i += 1
|
||||||
|
return data
|
||||||
|
|
||||||
|
def writeShortcuts(apps, shortcutFile):
|
||||||
|
encoding = "utf-8"
|
||||||
|
data = bytearray([0x00])
|
||||||
|
for app in apps:
|
||||||
|
if type(app) == dict:
|
||||||
|
data += bytearray("shortcuts", encoding)
|
||||||
|
data.append(0x00)
|
||||||
|
data += bytearray(apps[0]["shortcuts"], encoding)
|
||||||
|
else:
|
||||||
|
data.append(0x00)
|
||||||
|
data.append(0x02)
|
||||||
|
data += bytearray("appid", encoding)
|
||||||
|
data.append(0x00)
|
||||||
|
data += int(app.appId).to_bytes(4, "little")
|
||||||
|
data.append(0x01)
|
||||||
|
data += bytearray("AppName", encoding)
|
||||||
|
data.append(0x00)
|
||||||
|
data += bytearray(app.name, encoding)
|
||||||
|
data.append(0x00)
|
||||||
|
data.append(0x01)
|
||||||
|
data += bytearray("Exe", encoding)
|
||||||
|
data.append(0x00)
|
||||||
|
data += bytearray(app.executable, encoding)
|
||||||
|
data.append(0x00)
|
||||||
|
data.append(0x01)
|
||||||
|
data += bytearray("StartDir", encoding)
|
||||||
|
data.append(0x00)
|
||||||
|
data += bytearray(app.start, encoding)
|
||||||
|
data.append(0x00)
|
||||||
|
data.append(0x01)
|
||||||
|
data += bytearray("icon", encoding)
|
||||||
|
data.append(0x00)
|
||||||
|
data += bytearray(app.icon, encoding)
|
||||||
|
data.append(0x00)
|
||||||
|
data.append(0x01)
|
||||||
|
data += bytearray("ShortcutPath", encoding)
|
||||||
|
data.append(0x00)
|
||||||
|
data += bytearray(app.shortcut, encoding)
|
||||||
|
data.append(0x00)
|
||||||
|
data.append(0x01)
|
||||||
|
data += bytearray("LaunchOptions", encoding)
|
||||||
|
data.append(0x00)
|
||||||
|
data += bytearray(app.options, encoding)
|
||||||
|
data.append(0x00)
|
||||||
|
data.append(0x02)
|
||||||
|
data += bytearray("IsHidden", encoding)
|
||||||
|
data.append(0x00)
|
||||||
|
data += int(app.hidden).to_bytes(3, "little")
|
||||||
|
data.append(0x00)
|
||||||
|
data.append(0x02)
|
||||||
|
data += bytearray("AllowDesktopConfig", encoding)
|
||||||
|
data.append(0x00)
|
||||||
|
data += int(app.desktopConfig).to_bytes(3, "little")
|
||||||
|
data.append(0x00)
|
||||||
|
data.append(0x02)
|
||||||
|
data += bytearray("AllowOverlay", encoding)
|
||||||
|
data.append(0x00)
|
||||||
|
data += int(app.overlay).to_bytes(3, "little")
|
||||||
|
data.append(0x00)
|
||||||
|
data.append(0x02)
|
||||||
|
data += bytearray("OpenVR", encoding)
|
||||||
|
data.append(0x00)
|
||||||
|
data += int(app.vr).to_bytes(3, "little")
|
||||||
|
data.append(0x00)
|
||||||
|
data.append(0x02)
|
||||||
|
data += bytearray("Devkit", encoding)
|
||||||
|
data.append(0x00)
|
||||||
|
data += int(app.devkit).to_bytes(3, "little")
|
||||||
|
data.append(0x00)
|
||||||
|
data.append(0x01)
|
||||||
|
data += bytearray("DevkitGameID", encoding)
|
||||||
|
data.append(0x00)
|
||||||
|
data += bytearray(app.devkitId, encoding)
|
||||||
|
data.append(0x00)
|
||||||
|
data.append(0x02)
|
||||||
|
data += bytearray("LastPlayTime", encoding)
|
||||||
|
data.append(0x00)
|
||||||
|
data += int(app.playTime).to_bytes(4, "little")
|
||||||
|
data.append(0x00)
|
||||||
|
data += bytearray("tags", encoding)
|
||||||
|
data.append(0x00)
|
||||||
|
data += bytearray(app.tags, encoding)
|
||||||
|
with open(shortcutFile, 'wb') as stream:
|
||||||
|
stream.write(data)
|
||||||
|
|
||||||
|
def getSteamArgs():
|
||||||
|
global steamClient
|
||||||
|
if not steamClient:
|
||||||
|
args = Namespace()
|
||||||
|
args.user = None
|
||||||
|
args.anonymous = True
|
||||||
|
args.skip_licenses = True
|
||||||
|
steamClient = CachingSteamClient()
|
||||||
|
print("Logging into steam")
|
||||||
|
steamClient.login_from_args(args)
|
||||||
|
return steamClient
|
||||||
|
|
||||||
|
def getHeader(code):
|
||||||
|
return "https://cdn.cloudflare.steamstatic.com/steam/apps/%s/header.jpg" % code
|
||||||
|
|
||||||
|
def getLibraryTall(code):
|
||||||
|
return "https://cdn.cloudflare.steamstatic.com/steam/apps/%s/library_600x900.jpg" % code
|
||||||
|
|
||||||
|
def getBackground(code):
|
||||||
|
return "https://cdn.cloudflare.steamstatic.com/steam/apps/%s/library_hero.jpg" % code
|
||||||
|
|
||||||
|
def getLogo(code):
|
||||||
|
return "https://cdn.cloudflare.steamstatic.com/steam/apps/%s/logo.png" % code
|
||||||
|
|
||||||
|
def getGameInfo(code):
|
||||||
|
fromContent = requests.get(fromURL, allow_redirects=True)
|
||||||
|
return "https://cdn.cloudflare.steamstatic.com/steam/apps/%s/logo.png" % code
|
||||||
|
|
||||||
|
def copyContent(name, content, byte=True):
|
||||||
|
userdata = Path(Path.home(), ".steam", "root", "userdata")
|
||||||
|
for user in userdata.iterdir():
|
||||||
|
toFile = Path(user, name)
|
||||||
|
print("Writing to %s" % (toFile))
|
||||||
|
mode = 'w'
|
||||||
|
if byte:
|
||||||
|
mode += 'b'
|
||||||
|
with open(toFile, mode) as stream:
|
||||||
|
stream.write(content)
|
||||||
|
|
||||||
|
def copyFile(fromURL, toCode):
|
||||||
|
fromURLComp = fromURL.split('/')
|
||||||
|
fromName = fromURLComp[6]
|
||||||
|
fromCode = fromURLComp[5]
|
||||||
|
userdata = Path(Path.home(), ".steam", "root", "userdata")
|
||||||
|
|
||||||
|
# Gets the final file name
|
||||||
|
if fromName == "library_hero.jpg":
|
||||||
|
toName = "_hero.jpg"
|
||||||
|
elif fromName == "library_600x900.jpg":
|
||||||
|
toName = "p.jpg"
|
||||||
|
elif fromName == "header.jpg":
|
||||||
|
toName = ".jpg"
|
||||||
|
else:
|
||||||
|
toName = "_" + fromName
|
||||||
|
toName = toCode + toName
|
||||||
|
|
||||||
|
print("Copying %s (%s) to %s" % (fromName, fromCode, toName))
|
||||||
|
fromContent = requests.get(fromURL, allow_redirects=True)
|
||||||
|
copyContent(Path("config", "grid", toName), fromContent.content)
|
||||||
|
|
||||||
|
def copyContents(ids):
|
||||||
|
client = getSteamArgs()
|
||||||
|
fromCodes = []
|
||||||
|
toCodes = []
|
||||||
|
reverse = {}
|
||||||
|
for id in ids:
|
||||||
|
fromCode = id[0]
|
||||||
|
toCode = id[1]
|
||||||
|
reverse[toCode] = fromCode
|
||||||
|
fromCodes.append(fromCode)
|
||||||
|
toCodes.append(toCode)
|
||||||
|
copyFile(getHeader(str(fromCode)), str(toCode))
|
||||||
|
copyFile(getLibraryTall(str(fromCode)), str(toCode))
|
||||||
|
copyFile(getBackground(str(fromCode)), str(toCode))
|
||||||
|
copyFile(getLogo(str(fromCode)), str(toCode))
|
||||||
|
data = client.get_product_info(apps=fromCodes)
|
||||||
|
i = 0
|
||||||
|
for i in range(len(ids)):
|
||||||
|
logo = data["apps"][ids[i][0]]["common"]["library_assets"]["logo_position"]
|
||||||
|
logo2 = {
|
||||||
|
"nversion": 1,
|
||||||
|
"logoPosition": {
|
||||||
|
"pinnedPosition": logo["pinned_position"],
|
||||||
|
"nWidthPct": logo["width_pct"],
|
||||||
|
"nHeightPct": logo["height_pct"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
copyContent(Path("config", "grid", str(ids[i][1]) + ".json"), json.dumps(logo2), byte=False)
|
||||||
|
|
||||||
|
logo2 = [[
|
||||||
|
"achievements", {
|
||||||
|
"version":2,
|
||||||
|
"data": {
|
||||||
|
"vecHighlight": [],
|
||||||
|
"vecUnachieved": [],
|
||||||
|
"vecAchievedHidden": [],
|
||||||
|
"nTotal": 0,
|
||||||
|
"nAchieved": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
], [
|
||||||
|
"customimage", {
|
||||||
|
"version": 1,
|
||||||
|
"data": logo2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
copyContent(Path("config", "librarycache", str(ids[i][1]) + ".json"), json.dumps(logo2), byte=False)
|
||||||
|
|
||||||
|
userdata = Path(Path.home(), ".steam", "root", "userdata")
|
||||||
|
for user in userdata.iterdir():
|
||||||
|
shortcutData = parseShortcuts(Path(user, "config", "shortcuts.vdf"))
|
||||||
|
for app in shortcutData:
|
||||||
|
if type(app) != dict:
|
||||||
|
if app.appId in toCodes:
|
||||||
|
print("Setting shortcut data for %s", app.name)
|
||||||
|
app.name = data["apps"][reverse[app.appId]]["common"]["name"]
|
||||||
|
app.icon = str(Path(user, "config", "grid", str(app.appId) + ".jpg"))
|
||||||
|
print("Setting %s to %s" % (app.name, app.icon))
|
||||||
|
writeShortcuts(shortcutData, Path(user, "config", "shortcuts.vdf"))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
keys = []
|
||||||
|
ids = []
|
||||||
|
ops = []
|
||||||
|
for term in sys.argv[1:]:
|
||||||
|
if not term.startswith('-'):
|
||||||
|
keys.append(term)
|
||||||
|
if term.isnumeric():
|
||||||
|
if len(ids) > 0 and type(ids[-1]) == int:
|
||||||
|
ids[-1] = (ids[-1], int(term))
|
||||||
|
else:
|
||||||
|
ids.append(int(term))
|
||||||
|
else:
|
||||||
|
ids = None
|
||||||
|
else:
|
||||||
|
if term[1] == '-':
|
||||||
|
ops.append(term[2:])
|
||||||
|
else:
|
||||||
|
for op in term[1:]:
|
||||||
|
ops.append(op)
|
||||||
|
if len(keys) < 2 and not "a" in ops:
|
||||||
|
print("%s [OPTIONS] (<fromId> <toId>)..." % __file__)
|
||||||
|
print("%s [OPTIONS] <term>..." % __file__)
|
||||||
|
exit(1)
|
||||||
|
if ids:
|
||||||
|
if type(ids[-1]) == int:
|
||||||
|
print("Must have an even number of fromID - toId pairs")
|
||||||
|
exit(2)
|
||||||
|
|
||||||
|
copyContents(ids)
|
||||||
|
else:
|
||||||
|
tempApps = {}
|
||||||
|
apps = []
|
||||||
|
userdata = Path(Path.home(), ".steam", "root", "userdata")
|
||||||
|
for user in userdata.iterdir():
|
||||||
|
shortcutData = parseShortcuts(Path(user, "config", "shortcuts.vdf"))
|
||||||
|
for app in shortcutData:
|
||||||
|
if type(app) != dict:
|
||||||
|
if "a" in ops:
|
||||||
|
apps.append(app)
|
||||||
|
else:
|
||||||
|
for term in keys:
|
||||||
|
if term.lower() in app.name.lower():
|
||||||
|
if not app in tempApps:
|
||||||
|
tempApps[app] = 1
|
||||||
|
else:
|
||||||
|
tempApps[app] += 1
|
||||||
|
if not "a" in ops:
|
||||||
|
for app in tempApps:
|
||||||
|
if tempApps[app] / len(keys) >= 0.7:
|
||||||
|
apps.append(app)
|
||||||
|
if len(apps) == 0:
|
||||||
|
if "v" in ops:
|
||||||
|
print("No matches found")
|
||||||
|
exit(0)
|
||||||
|
elif len(apps) == 1 and not "v" in ops and not "l" in ops:
|
||||||
|
print(apps[0].appId)
|
||||||
|
else:
|
||||||
|
for app in apps:
|
||||||
|
print("%d - %s" % (app.appId, app.name))
|
||||||
|
if "l" in ops:
|
||||||
|
print("\tExecutable: %s" % app.executable)
|
||||||
|
print("\tRuntime Directory: %s" % app.start)
|
||||||
|
print("\tIcon: %s" % app.icon)
|
||||||
|
print("\tShortcut: %s" % app.shortcut)
|
||||||
|
print("\tRuntime Options: %s" % app.options)
|
||||||
|
print("\tIs Hidden: %s" % bool(app.hidden))
|
||||||
|
print("\tDesktop Configuration: %s" % bool(app.desktopConfig))
|
||||||
|
print("\tOverlay Enabled: %s" % bool(app.overlay))
|
||||||
|
print("\tIs VR: %s" % bool(app.vr))
|
||||||
|
print("\tDevkit: %d" % app.devkit)
|
||||||
|
print("\tDevkit ID: %s" % app.devkitId)
|
||||||
|
print("\tDate of last Play: %s" % app.playTime)
|
||||||
|
print("\tTags: %s" % app.tags)
|
||||||
Reference in New Issue
Block a user