mirror of
https://github.com/saphid/frame-control.git
synced 2026-10-06 02:00:19 +02:00
Address review findings in the app, server and tests
- app: startup shell, python and ssh probes run asynchronously so a slow shell profile can't freeze the window; PATH comes from the user's real login shell and a failed lookup isn't cached; a server that never answers is killed; the setup offer runs once per launch, only after the UI loads, and decides from HostName alone; connect.sh is started through `env ... zsh` so it works whatever the login shell is. - server: volume validates the level before muting or changing anything. - Steam: null-safe install-manager fields, http.client errors caught in store ratings, price fallback when a sale has no final price. - tests: server output kept for diagnosis, any startup error retried, and captures asserted non-cacheable. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
d4486a7681
commit
99653151c4
13 files changed
+340
-38
No files matched your search
+2
-2
@@ -142,8 +142,8 @@ OWNED_JS = r"""
|
||||
"""
|
||||
|
||||
WIZARD_JS = """SteamClient.Installs.GetInstallManagerInfo().then(i => ({
|
||||
state: i.eInstallState, app: i.currentAppID, need: i.nDiskSpaceRequired, free: i.nDiskSpaceAvailable,
|
||||
error: i.eAppError, detail: i.errorDetail }))"""
|
||||
state: i?.eInstallState ?? 0, app: i?.currentAppID ?? 0, need: i?.nDiskSpaceRequired || 0,
|
||||
free: i?.nDiskSpaceAvailable || 0, error: i?.eAppError, detail: i?.errorDetail }))"""
|
||||
|
||||
|
||||
def steam_url(url):
|
||||
|
||||
+2
-1
@@ -6,6 +6,7 @@ Uses the store's public endpoints (no key, no login):
|
||||
`frame_resolved_category` is the Frame's
|
||||
Buying happens on the store page, signed in as the user; nothing here buys.
|
||||
"""
|
||||
import http.client
|
||||
import json
|
||||
import threading
|
||||
import time
|
||||
@@ -37,7 +38,7 @@ def frame_rating(appid):
|
||||
r = _get("saleaction/ajaxgetdeckappcompatibilityreport", {"nAppID": appid, "l": "english"})
|
||||
cat = int((r.get("results") or {}).get("frame_resolved_category") or 0)
|
||||
cat = cat if 0 <= cat <= 3 else 0
|
||||
except (OSError, ValueError, TypeError, AttributeError):
|
||||
except (OSError, ValueError, TypeError, AttributeError, http.client.HTTPException):
|
||||
return 0 # not cached, so the next search retries
|
||||
with _lock:
|
||||
_compat[appid] = (time.time(), cat)
|
||||
|
||||
+2
-1
@@ -1024,7 +1024,8 @@ function price(p) {
|
||||
if (!p) return "Free";
|
||||
const f = n => { try { return new Intl.NumberFormat(undefined, { style: "currency", currency: p.currency }).format(n / 100); }
|
||||
catch { return `${(n / 100).toFixed(2)} ${esc(p.currency || "")}`; } };
|
||||
return p.initial > p.final ? `<s>${f(p.initial)}</s>${f(p.final)}` : f(p.final);
|
||||
const fin = p.final ?? p.initial ?? 0;
|
||||
return p.initial > fin ? `<s>${f(p.initial)}</s>${f(fin)}` : f(fin);
|
||||
}
|
||||
|
||||
function gameButtons(id, name, free) {
|
||||
|
||||
+8
-4
@@ -8,6 +8,7 @@ Usage: ui/server.py [--port 47810] (normally started by scripts/frame-ui.sh)
|
||||
Env: FRAME_ALIAS (default frame)
|
||||
"""
|
||||
import argparse
|
||||
import http.client
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
@@ -204,7 +205,7 @@ def steam_frame(*args, timeout=40):
|
||||
except Failure as e:
|
||||
# frame_steam.py prints {"error": ...} on stdout when it fails, but ssh()
|
||||
# reports stderr instead if there was any, so look in both.
|
||||
for line in reversed([*getattr(e, "stdout", "").splitlines(), *str(e).splitlines()]):
|
||||
for line in [*reversed(getattr(e, "stdout", "").splitlines()), *reversed(str(e).splitlines())]:
|
||||
try:
|
||||
raise Failure(json.loads(line)["error"]) from None
|
||||
except (ValueError, KeyError, TypeError):
|
||||
@@ -230,17 +231,20 @@ def steam_search(query):
|
||||
raise Failure("cc must be a two-letter country code", 400)
|
||||
try:
|
||||
return {"results": frame_store.search((q.get("q") or [""])[0], cc)}
|
||||
except (OSError, ValueError, TypeError, AttributeError) as e:
|
||||
except (OSError, ValueError, TypeError, AttributeError, http.client.HTTPException) as e:
|
||||
raise Failure(f"Steam store search failed: {e}")
|
||||
|
||||
|
||||
def set_volume(body):
|
||||
if "muted" in body:
|
||||
ssh(f"wpctl set-mute @DEFAULT_AUDIO_SINK@ {1 if body['muted'] else 0}")
|
||||
# Validate everything before touching the headset.
|
||||
level = None
|
||||
if "level" in body:
|
||||
level = float(body["level"])
|
||||
if not 0 <= level <= 1:
|
||||
raise Failure("level must be 0..1", 400)
|
||||
if "muted" in body:
|
||||
ssh(f"wpctl set-mute @DEFAULT_AUDIO_SINK@ {1 if body['muted'] else 0}")
|
||||
if level is not None:
|
||||
ssh(f"wpctl set-volume @DEFAULT_AUDIO_SINK@ {level:.2f}")
|
||||
return {"message": "Volume updated"}
|
||||
|
||||
|
||||
Reference in new issue
Block a user