-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsteam_utils.py
More file actions
153 lines (126 loc) · 4.44 KB
/
Copy pathsteam_utils.py
File metadata and controls
153 lines (126 loc) · 4.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
"""
Shared Steam/system utility functions for CapyDeploy Decky Plugin.
All functions are module-level (no class required).
"""
import os
from pathlib import Path
from typing import Optional
import decky # type: ignore
def _is_usable_ipv4(ip: str) -> bool:
"""True if the IP is a non-loopback, non-link-local IPv4 address."""
if not ip or "." not in ip:
return False
if ip.startswith("127.") or ip.startswith("169.254."):
return False
return True
def get_local_ip() -> Optional[str]:
"""Get the local non-loopback IPv4 address.
Returns None when the network is not ready (no usable address yet),
so callers can decide whether to retry or fall back. Note: callers
that historically expected a string fallback (e.g. "127.0.0.1")
must be updated.
"""
import socket
# Primary: UDP-connect trick reveals the interface that would route
# outbound traffic. Fast and accurate when the network is up.
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(1.0)
s.connect(("8.8.8.8", 80))
ip = s.getsockname()[0]
s.close()
if _is_usable_ipv4(ip):
return ip
except Exception:
pass
# Fallback: enumerate addresses bound to the hostname.
try:
hostname = socket.gethostname()
for info in socket.getaddrinfo(hostname, None, socket.AF_INET):
ip = info[4][0]
if _is_usable_ipv4(ip):
return ip
except Exception:
pass
return None
def detect_platform() -> str:
"""Detect the handheld platform."""
# Check OS release first (most reliable method)
try:
with open("/etc/os-release", "r") as f:
content = f.read().lower()
# SteamOS is the real Steam Deck
if "steamos" in content:
return "steamdeck"
if "chimeraos" in content:
return "chimeraos"
# Bazzite is NOT a Steam Deck, return linux
if "bazzite" in content:
return "linux"
except Exception:
pass
# Check for handheld-specific files (fallback)
if os.path.exists("/usr/share/plymouth/themes/legion-go"):
return "legiongologo"
if os.path.exists("/usr/share/plymouth/themes/rogally"):
return "rogally"
# Only check /home/deck if it's a real directory (not a symlink)
# This avoids false positives on Bazzite which symlinks /home/deck
try:
info = os.lstat("/home/deck")
import stat
if not stat.S_ISLNK(info.st_mode) and stat.S_ISDIR(info.st_mode):
return "steamdeck"
except Exception:
pass
return "linux"
def get_user_home() -> str:
"""Get the real user home directory (not /root when running as service)."""
# Check the standard Steam Deck user first
if os.path.exists("/home/deck"):
return "/home/deck"
try:
for entry in os.listdir("/home"):
home_path = f"/home/{entry}"
if os.path.isdir(home_path) and os.path.exists(f"{home_path}/.steam"):
return home_path
except Exception:
pass
return str(Path.home())
def expand_path(path: str) -> str:
"""Expand ~ to actual home directory."""
if path.startswith("~/"):
return os.path.join(get_user_home(), path[2:])
return path
def get_steam_dir() -> Optional[str]:
"""Find Steam installation directory."""
home = get_user_home()
candidates = [
os.path.join(home, ".steam", "steam"),
os.path.join(home, ".local", "share", "Steam"),
os.path.join(home, ".var", "app", "com.valvesoftware.Steam", ".steam", "steam"),
]
for path in candidates:
if os.path.isdir(path):
return path
return None
def get_steam_users() -> list:
"""Get Steam users from userdata directory."""
steam_dir = get_steam_dir()
if not steam_dir:
return []
userdata_dir = os.path.join(steam_dir, "userdata")
if not os.path.isdir(userdata_dir):
return []
users = []
for entry in os.listdir(userdata_dir):
entry_path = os.path.join(userdata_dir, entry)
if not os.path.isdir(entry_path):
continue
if not entry.isdigit() or entry == "0":
continue
has_shortcuts = os.path.exists(
os.path.join(entry_path, "config", "shortcuts.vdf")
)
users.append({"id": entry, "hasShortcuts": has_shortcuts})
return users