Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ gevent-eventemitter ~= 2.1
protobuf ~= 5.29.3
wsproto ~= 1.2.0
pillow == 12.1.1
PyGObject == 3.56.2
53 changes: 47 additions & 6 deletions steamiconfixer/compat/linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,21 @@
from steam.enums.emsg import EMsg
from PIL import Image
from pathlib import Path
import subprocess

from ..types import Icon

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

usage = """---------------------------------------------

Usage:
sif <path to file> [path to icons]

Examples:
sif ~/.local/share/applications
sif ~/Desktop
sif ~/Desktop/Steam Games
sif ~/.local/share/applications $HOME/.icons
Expand All @@ -43,15 +49,25 @@
Incompatible operating system (exit code 100)
"""

gtk_sizes = [16, 32, 48, 64, 128, 256, 512, 1024, 2048]
gtk_user_path = os.path.expandvars('$HOME/.local/share/icons/hicolor')
gtk_icon_theme = Gtk.IconTheme.get_default()

steamapi = SteamClient()
steamapi.anonymous_login()

def refreshiconcache():
print("Refreshing icon cache")
Path(gtk_user_path).touch()
subprocess.run(["gtk-update-icon-cache"])

def isshortcut(filename):
return filename.name.endswith(".desktop")

def setupiconpath(filename):
if len(filename) == 0:
filename = os.path.expandvars("$HOME/.icons")
# Empty path signifies use Gtk icon paths
return ''
if not os.path.exists(filename):
os.makedirs(filename)
return filename
Expand All @@ -71,14 +87,24 @@ def readshortcut(filename):
iconpathmatch = re.search(r"Icon=([^\n]*)\n", contents)

if steamidmatch == None or iconpathmatch == None:
print(colored(filename.name + ": Shortcut doesn't appear to be a Steam shortcut. Skipping.", "yellow"))
print(colored(filename.name + ": Shortcut doesn't appear to be a Steam shortcut. Skipping.", "blue"))
return None

steamid = steamidmatch.group(1)
iconpath = iconpathmatch.group(1)

# Check if the icon exists
if iconpath != "steam" and os.path.exists(iconpath):
if iconpath == "steam":
pass
elif not Path(iconpath).is_absolute():
# Ex: "steam_icon_440" can be valid as long as "steam_icon_440.png" exists within a valid icon search directory
gtk_icon_exists = gtk_icon_theme.has_icon(iconpath)
gtk_icon = gtk_icon_theme.lookup_icon(iconpath, 256, 0)
if gtk_icon_theme.has_icon(iconpath):
print(colored(filename.name + ": Icon file is present, nothing needs to be done. Skipping.", "green"))
return None
elif os.path.exists(iconpath):
# Absolute icon paths to anywhere are also valid, however certain directories are recommended, such as $HOME/.icons
if not os.path.isfile(iconpath):
print(colored(filename.name + ": Icon path is a directory. This error must be fixed manually. Skipping.", "red"))
return None
Expand All @@ -100,10 +126,25 @@ def readshortcut(filename):
return Icon(steamid, iconpath, iconname, filename)

def writeicon(icon, response, iconpath):
# Empty iconpath means use Gtk icons
img = Image.open(io.BytesIO(response.content))
savepath = Path(os.path.join(iconpath, "steamicon_" + Path(icon.name).stem + ".png")).resolve()
img.save(savepath, "PNG")
return str(savepath)
if len(iconpath) == 0:
name = "steam_icon_" + icon.steamid
width, height = img.size
for x in gtk_sizes:
if width < x and height < x:
break
full_dir = os.path.join(gtk_user_path, str(x) + "x" + str(x) + "/apps")
if not os.path.exists(full_dir):
os.makedirs(full_dir)
thumb = img.copy()
thumb.thumbnail((x, x), Image.LANCZOS)
thumb.save(os.path.join(full_dir, name + ".png"), "PNG")
return name
else:
savepath = Path(os.path.join(iconpath, "steam_icon_" + icon.steamid + ".png")).resolve()
img.save(savepath, "PNG")
return str(savepath)

def updateshortcuts(icon, searchpath, iconpath):
with open(icon.shortcutfilename, "r") as file:
Expand Down
6 changes: 5 additions & 1 deletion steamiconfixer/compat/windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
Incompatible operating system (exit code 100)
"""

def refreshiconcache():
# Unneeded on Windows
pass

def isshortcut(filename):
return filename.name.endswith(".url")

Expand All @@ -59,7 +63,7 @@ def readshortcut(filename):
iconnamematch = re.search(r"\\([^\n\\]*)\n", contents)

if steamidmatch == None or iconpathmatch == None or iconnamematch == None:
print(colored(filename.name + ": Shortcut doesn't appear to be a Steam shortcut. Skipping.", "yellow"))
print(colored(filename.name + ": Shortcut doesn't appear to be a Steam shortcut. Skipping.", "blue"))
return None

steamid = steamidmatch.group(1)
Expand Down
29 changes: 25 additions & 4 deletions steamiconfixer/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@
from steamiconfixer.compat import compat

icons = {}
baseurl = "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/apps/"
# baseurl = "https://shared.fastly.steamstatic.com/community_assets/images/apps/" # Alternative URL
baseurls = [
"https://steamcdn-a.akamaihd.net/steamcommunity/public/images/apps/",
"https://shared.fastly.steamstatic.com/community_assets/images/apps/",
]

license = """\nSteam Icon Fixer, Version 1.1
Copyright (C) 2026 Liam "AyesC" Hogan
Expand Down Expand Up @@ -75,6 +77,8 @@
# Create list of icons
print("Searching for valid Steam shortcuts in " + searchpath + "...")

compat.refreshiconcache()

for filename in os.scandir(searchpath):
# Igore directories or any files that are not .url or .desktop files
if not filename.is_file():
Expand Down Expand Up @@ -118,14 +122,29 @@

errors = 0

current_baseurl_index = 0
current_baseurl = baseurls[current_baseurl_index]

for steamid, icon in icons.items():
# Create the URL and make a request
url = baseurl + steamid + "/" + icon.name
url = current_baseurl + steamid + "/" + icon.name
response = requests.get(url)

while not response.ok:
print(colored("Got code " + str(response.status_code) + " at CDN " + current_baseurl, "red"))
current_baseurl_index = current_baseurl_index + 1
if current_baseurl_index < len(baseurls):
current_baseurl = baseurls[current_baseurl_index]
print(colored("Retrying with CDN " + current_baseurl, "red"))
url = current_baseurl + steamid + "/" + icon.name
response = requests.get(url)
else:
print(colored("All CDNs failed to respond", "red"))
sys.exit(0)

# Check if response was ok
if not response.ok:
print(colored(steamid + ": Failed to download icon. Response code was " + response.status_code + ".", "red"))
print(colored(steamid + ": Failed to download icon. Response code was " + str(response.status_code) + ".", "red"))
errors = errors + 1
continue

Expand All @@ -149,3 +168,5 @@
print(colored(steamid + ": Downloaded and saved successfully.", "green"))

print("\nDownloading completed with " + str(errors) + " errors. Refer to the above log for details.")

compat.refreshiconcache()