testsAndMisc/linux_configuration/scripts/single_use/utils/lib/steam_api.sh
Krzysztof kuhy Rudnicki 2c95425a60 steam_compatibility: split 663 -> 140 + 199 + 156 + 124 + 103
Grouped by the state each function touches rather than by size:

  steam_requirements.sh  199  requirement parsing, scoring, and the hardware
                              detection that feeds it
  steam_report.sh        156  the run loop and the cache it reads
  steam_api.sh           124  library discovery, Steam and ProtonDB calls
  steam_credentials.sh   103  credentials, usage text, presentation helpers

The grouping was forced by the SC2034 writer/reader rule, not chosen. Two
arrangements were discarded: detect_system alone in a lib writes the three
SYSTEM_* globals without reading them, and load_cache_map alone writes
CACHE_MAP without reading it. Both were fixed by putting the writer next to
its reader - detect_system beside score_game, load_cache_map beside main.

Verified: all 33 functions verbatim; every lib passes shellcheck standalone
with no suppression; and the script actually runs - `--help` prints the usage
block and exits 0, which is what proves the four source lines resolve and
main still dispatches.

Over-cap files 26 -> 25.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
2026-08-17 14:32:41 +02:00

125 lines
4.6 KiB
Bash
Executable File

#!/usr/bin/env bash
# Steam library discovery, the Steam and ProtonDB API calls, and requirement
# extraction for steam_compatibility.sh.
find_steamapps_dirs() {
local dirs=()
for base in "${STEAM_DIRS[@]}"; do
[[ -d $base ]] || continue
if [[ -f "$base/steamapps/libraryfolders.vdf" ]]; then
# Newer format includes nested objects with paths
local paths
paths=$(grep -oE '"path"\s+"[^"]+"' "$base/steamapps/libraryfolders.vdf" | sed -E 's/.*"([^"]+)"/\1/' || true)
if [[ -n $paths ]]; then
while IFS= read -r p; do
[[ -d "$p/steamapps" ]] && dirs+=("$p/steamapps")
done <<< "$paths"
fi
fi
[[ -d "$base/steamapps" ]] && dirs+=("$base/steamapps")
done
# de-dupe
printf "%s\n" "${dirs[@]}" 2> /dev/null | awk '!seen[$0]++'
}
list_installed_games() {
local d appid name
while IFS= read -r d; do
[[ -d $d ]] || continue
for mf in "$d"/appmanifest_*.acf; do
[[ -f $mf ]] || continue
appid=$(grep -oE '"appid"\s+"[0-9]+"' "$mf" | sed -E 's/.*"([0-9]+)"/\1/' | head -n1)
name=$(grep -oE '"name"\s+"[^"]+"' "$mf" | sed -E 's/.*"([^"]+)"/\1/' | head -n1)
if [[ -n $appid ]]; then
printf "%s\t%s\n" "$appid" "${name:-Unknown}"
fi
done
done < <(find_steamapps_dirs)
}
list_owned_games_via_api() {
local key="${STEAM_API_KEY:-}" sid="${STEAM_ID64:-}"
if [[ -z $key || -z $sid ]]; then
return 1
fi
local url="https://api.steampowered.com/IPlayerService/GetOwnedGames/v0001/?key=${key}&steamid=${sid}&include_appinfo=1&include_played_free_games=1&format=json"
http_get "$url" | jq -r '.response.games[]? | "\(.appid)\t\(.name)"' || return 1
}
fetch_appdetails_json() {
local appid="$1"
# Using store API (no key)
local url="https://store.steampowered.com/api/appdetails?appids=${appid}&l=en&cc=us"
http_get "$url" || true
}
extract_requirements_and_platforms() {
# Input: JSON from appdetails; Output: TSV of fields
# Fields: success, linux, windows, mac, min_text, rec_text, type
local appid="$1" json="$2"
# Some apps return {"APPID": {"success":true, "data":{...}}}
local out
out=$(jq -r --arg APP "$appid" '
.[$APP] as $root | if ($root.success==true and ($root.data|type)=="object") then
($root.data.platforms.linux // false) as $linux |
($root.data.platforms.windows // false) as $windows |
($root.data.platforms.mac // false) as $mac |
($root.data.type // "") as $type |
# Prefer Linux reqs when present
($root.data.linux_requirements.minimum // $root.data.pc_requirements.minimum // "") as $min |
($root.data.linux_requirements.recommended // $root.data.pc_requirements.recommended // "") as $rec |
["ok", ($linux|tostring), ($windows|tostring), ($mac|tostring), ($min|tostring), ($rec|tostring), ($type|tostring)] | @tsv
else
["fail", "false", "false", "false", "", "", ""] | @tsv
end' 2> /dev/null <<< "$json") || true
if [[ -z $out ]]; then
out=$'fail false false false '
fi
printf '%s\n' "$out"
}
# Read JSON from stdin (avoids storing large/binary data in variables)
extract_requirements_and_platforms_stdin() {
local appid="$1"
local out
out=$(jq -r --arg APP "$appid" '
.[$APP] as $root | if ($root.success==true and ($root.data|type)=="object") then
($root.data.platforms.linux // false) as $linux |
($root.data.platforms.windows // false) as $windows |
($root.data.platforms.mac // false) as $mac |
($root.data.type // "") as $type |
($root.data.linux_requirements.minimum // $root.data.pc_requirements.minimum // "") as $min |
($root.data.linux_requirements.recommended // $root.data.pc_requirements.recommended // "") as $rec |
["ok", ($linux|tostring), ($windows|tostring), ($mac|tostring), ($min|tostring), ($rec|tostring), ($type|tostring)] | @tsv
else
["fail", "false", "false", "false", "", "", ""] | @tsv
end' 2> /dev/null) || true
if [[ -z $out ]]; then
out=$'fail\tfalse\tfalse\tfalse\t\t\t'
fi
printf '%s\n' "$out"
}
fetch_protondb_tier() {
local appid="$1"
local url="https://www.protondb.com/api/v1/reports/summaries/${appid}.json"
# Returns minimal JSON including .tier, .confidence; we only need .tier
local tier
tier=$(http_get "$url" | jq -r 'try .tier // "unknown"' 2> /dev/null || true)
if [[ -z $tier || $tier == "null" ]]; then
echo "unknown"
else
tr '[:upper:]' '[:lower:]' <<< "$tier" | tr -d '\r\n'
fi
}
protondb_allowed() {
local tier
tier=$(tr '[:upper:]' '[:lower:]' <<< "${1:-}")
case "$tier" in
platinum | native | gold | silver | unknown | "") return 0 ;;
bronze | pending | borked | unsupported | broken) return 1 ;;
*) return 0 ;;
esac
}