mirror of
https://github.com/kuhyx/steam-backlog-enforcer.git
synced 2026-07-04 13:23:18 +02:00
refactor(steam-backlog): move hide retry loop into single JS evaluation
SetAppsAsHidden is unreliable for large libraries — silently drops operations. Running the entire retry loop (max 30 passes, batch 50, 200ms settle delay) inside a single CDP Runtime.evaluate converges to 0 remaining visible games.
This commit is contained in:
parent
482845dd25
commit
06b3674133
@ -212,51 +212,81 @@ def ensure_steam_debug_port() -> None:
|
|||||||
# ──────────────────────────────────────────────────────────────
|
# ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
|
_HIDE_BATCH_SIZE = 50
|
||||||
|
_MAX_HIDE_PASSES = 30
|
||||||
|
_SETTLE_DELAY_MS = 200
|
||||||
|
|
||||||
|
|
||||||
def hide_other_games(
|
def hide_other_games(
|
||||||
owned_app_ids: list[int],
|
owned_app_ids: list[int],
|
||||||
allowed_app_id: int | None,
|
allowed_app_id: int | None,
|
||||||
) -> int:
|
) -> int:
|
||||||
"""Hide every owned game except *allowed_app_id* in the Steam library.
|
"""Hide every game except *allowed_app_id* in the Steam library.
|
||||||
|
|
||||||
Uses the Chrome DevTools Protocol to call
|
Uses the Chrome DevTools Protocol to call
|
||||||
``collectionStore.SetAppsAsHidden()`` in Steam's JS context.
|
``collectionStore.SetAppsAsHidden()`` in Steam's JS context.
|
||||||
Changes take effect immediately — no restart required.
|
|
||||||
|
|
||||||
Returns the number of games newly hidden.
|
The entire retry loop runs inside a single JS evaluation to avoid
|
||||||
|
WebSocket round-trip overhead. ``SetAppsAsHidden`` is unreliable
|
||||||
|
in a single pass for large libraries, so the JS loop retries until
|
||||||
|
``visibleApps`` converges to only the allowed game.
|
||||||
|
|
||||||
|
On the first pass, caller-provided *owned_app_ids* are included to
|
||||||
|
cover games that might not yet appear in ``visibleApps`` due to
|
||||||
|
stale MobX state.
|
||||||
|
|
||||||
|
Returns the total number of games hidden across all passes.
|
||||||
"""
|
"""
|
||||||
ensure_steam_debug_port()
|
ensure_steam_debug_port()
|
||||||
|
|
||||||
hide_ids = sorted(aid for aid in owned_app_ids if aid != allowed_app_id)
|
allowed_js = str(allowed_app_id) if allowed_app_id is not None else "null"
|
||||||
if not hide_ids:
|
extra_ids = sorted(aid for aid in owned_app_ids if aid != allowed_app_id)
|
||||||
return 0
|
extra_json = json.dumps(extra_ids)
|
||||||
|
|
||||||
ids_json = json.dumps(hide_ids)
|
|
||||||
js = f"""
|
js = f"""
|
||||||
(() => {{
|
(async () => {{
|
||||||
const toHide = {ids_json};
|
const allowed = {allowed_js};
|
||||||
const already = new Set();
|
const coll = collectionStore.allGamesCollection;
|
||||||
const hidden = collectionStore.GetCollection('hidden');
|
const extraIds = {extra_json};
|
||||||
if (hidden && hidden.allApps) {{
|
let totalHidden = 0;
|
||||||
for (const app of hidden.allApps) already.add(app.appid);
|
const maxPasses = {_MAX_HIDE_PASSES};
|
||||||
|
const batchSize = {_HIDE_BATCH_SIZE};
|
||||||
|
|
||||||
|
for (let pass = 0; pass < maxPasses; pass++) {{
|
||||||
|
let visible = coll && coll.visibleApps
|
||||||
|
? coll.visibleApps.map(a => a.appid).filter(id => id !== allowed)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
if (pass === 0) {{
|
||||||
|
const visSet = new Set(visible);
|
||||||
|
for (const id of extraIds) {{
|
||||||
|
if (!visSet.has(id)) visible.push(id);
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
|
||||||
|
if (visible.length === 0) break;
|
||||||
|
|
||||||
|
for (let i = 0; i < visible.length; i += batchSize) {{
|
||||||
|
const batch = visible.slice(i, i + batchSize);
|
||||||
|
await collectionStore.SetAppsAsHidden(batch, true);
|
||||||
|
totalHidden += batch.length;
|
||||||
|
}}
|
||||||
|
|
||||||
|
await new Promise(r => setTimeout(r, {_SETTLE_DELAY_MS}));
|
||||||
}}
|
}}
|
||||||
const newIds = toHide.filter(id => !already.has(id));
|
|
||||||
if (newIds.length > 0) {{
|
if (allowed !== null) {{
|
||||||
collectionStore.SetAppsAsHidden(newIds, true);
|
await collectionStore.SetAppsAsHidden([allowed], false);
|
||||||
}}
|
}}
|
||||||
// Unhide the allowed game if it was hidden.
|
|
||||||
const allowedId = {allowed_app_id if allowed_app_id is not None else "null"};
|
return JSON.stringify({{ totalHidden }});
|
||||||
if (allowedId !== null && collectionStore.BIsHidden(allowedId)) {{
|
|
||||||
collectionStore.SetAppsAsHidden([allowedId], false);
|
|
||||||
}}
|
|
||||||
return JSON.stringify({{ newlyHidden: newIds.length }});
|
|
||||||
}})()
|
}})()
|
||||||
"""
|
"""
|
||||||
|
|
||||||
result = _evaluate_js(js)
|
result = _evaluate_js(js)
|
||||||
value = _cdp_result_value(result)
|
value = _cdp_result_value(result)
|
||||||
parsed = json.loads(value)
|
parsed = json.loads(value)
|
||||||
count: int = parsed["newlyHidden"]
|
count: int = parsed["totalHidden"]
|
||||||
logger.info("Hidden %d new games via CDP.", count)
|
logger.info("Hid %d games via CDP.", count)
|
||||||
return count
|
return count
|
||||||
|
|
||||||
|
|
||||||
@ -269,12 +299,12 @@ def unhide_all_games(owned_app_ids: list[int]) -> int:
|
|||||||
|
|
||||||
json.dumps(sorted(owned_app_ids))
|
json.dumps(sorted(owned_app_ids))
|
||||||
js = """
|
js = """
|
||||||
(() => {
|
(async () => {
|
||||||
const hidden = collectionStore.GetCollection('hidden');
|
const hidden = collectionStore.GetCollection('hidden');
|
||||||
if (!hidden || !hidden.allApps) return JSON.stringify({ count: 0 });
|
if (!hidden || !hidden.allApps) return JSON.stringify({ count: 0 });
|
||||||
const hiddenIds = hidden.allApps.map(a => a.appid);
|
const hiddenIds = hidden.allApps.map(a => a.appid);
|
||||||
if (hiddenIds.length === 0) return JSON.stringify({ count: 0 });
|
if (hiddenIds.length === 0) return JSON.stringify({ count: 0 });
|
||||||
collectionStore.SetAppsAsHidden(hiddenIds, false);
|
await collectionStore.SetAppsAsHidden(hiddenIds, false);
|
||||||
return JSON.stringify({ count: hiddenIds.length });
|
return JSON.stringify({ count: hiddenIds.length });
|
||||||
})()
|
})()
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -437,19 +437,33 @@ class TestHideOtherGames:
|
|||||||
),
|
),
|
||||||
patch(
|
patch(
|
||||||
"python_pkg.steam_backlog_enforcer.library_hider._evaluate_js",
|
"python_pkg.steam_backlog_enforcer.library_hider._evaluate_js",
|
||||||
return_value={"result": {"result": {"value": '{"newlyHidden": 5}'}}},
|
return_value={
|
||||||
|
"result": {"result": {"value": '{"totalHidden": 5}'}},
|
||||||
|
},
|
||||||
),
|
),
|
||||||
patch(
|
patch(
|
||||||
"python_pkg.steam_backlog_enforcer.library_hider._cdp_result_value",
|
"python_pkg.steam_backlog_enforcer.library_hider._cdp_result_value",
|
||||||
return_value='{"newlyHidden": 5}',
|
return_value='{"totalHidden": 5}',
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
count = hide_other_games([1, 2, 3], 1)
|
count = hide_other_games([1, 2, 3], 1)
|
||||||
assert count == 5
|
assert count == 5
|
||||||
|
|
||||||
def test_empty_list(self) -> None:
|
def test_empty_list(self) -> None:
|
||||||
with patch(
|
with (
|
||||||
"python_pkg.steam_backlog_enforcer.library_hider.ensure_steam_debug_port",
|
patch(
|
||||||
|
"python_pkg.steam_backlog_enforcer.library_hider.ensure_steam_debug_port",
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
"python_pkg.steam_backlog_enforcer.library_hider._evaluate_js",
|
||||||
|
return_value={
|
||||||
|
"result": {"result": {"value": '{"totalHidden": 0}'}},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
"python_pkg.steam_backlog_enforcer.library_hider._cdp_result_value",
|
||||||
|
return_value='{"totalHidden": 0}',
|
||||||
|
),
|
||||||
):
|
):
|
||||||
count = hide_other_games([1], 1)
|
count = hide_other_games([1], 1)
|
||||||
assert count == 0
|
assert count == 0
|
||||||
@ -461,11 +475,13 @@ class TestHideOtherGames:
|
|||||||
),
|
),
|
||||||
patch(
|
patch(
|
||||||
"python_pkg.steam_backlog_enforcer.library_hider._evaluate_js",
|
"python_pkg.steam_backlog_enforcer.library_hider._evaluate_js",
|
||||||
return_value={"result": {"result": {"value": '{"newlyHidden": 2}'}}},
|
return_value={
|
||||||
|
"result": {"result": {"value": '{"totalHidden": 2}'}},
|
||||||
|
},
|
||||||
),
|
),
|
||||||
patch(
|
patch(
|
||||||
"python_pkg.steam_backlog_enforcer.library_hider._cdp_result_value",
|
"python_pkg.steam_backlog_enforcer.library_hider._cdp_result_value",
|
||||||
return_value='{"newlyHidden": 2}',
|
return_value='{"totalHidden": 2}',
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
count = hide_other_games([1, 2], None)
|
count = hide_other_games([1, 2], None)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user