Files
homelab-docs/homelab/scripts/check_size_sources.py
2026-02-23 16:47:17 +03:00

129 lines
5.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
Сверяет кэш размеров (steam_app_sizes.json) с api.steamcmd.net.
Находит игры, у которых размер в кэше сильно меньше, чем по steamcmd —
скорее всего размер был взят из fallback (магазин Steam), а не из steamcmd.
Запуск: из homelab/scripts/
python3 check_size_sources.py — проверить все игры (~1015 мин)
python3 check_size_sources.py --sample 50 — только 50 для быстрой оценки
"""
import argparse
import json
import random
import time
from pathlib import Path
# используем ту же логику, что и steam_library_size
from steam_library_size import get_app_size_from_steamcmd
SCRIPT_DIR = Path(__file__).parent
CACHE_FILE = SCRIPT_DIR / "steam_app_sizes.json"
LIBRARY_FILE = SCRIPT_DIR / "steam_library.json"
REQUEST_DELAY = 0.5
# если в кэше меньше чем (steamcmd * MIN_RATIO), считаем источник сомнительным
MIN_RATIO = 0.6
# или если steamcmd даёт больше MIN_GB, а разница больше DIFF_GB
MIN_GB_STEAMCMD = 15.0
DIFF_GB = 10.0
def main(sample: int | None = None) -> None:
with open(CACHE_FILE, encoding="utf-8") as f:
cache = json.load(f)
cache_sizes = {int(k): v for k, v in cache.items()}
if LIBRARY_FILE.exists():
with open(LIBRARY_FILE, encoding="utf-8") as f:
library = json.load(f)
name_by_appid = {g["appid"]: g.get("name", "?") for g in library}
else:
name_by_appid = {}
items = list(cache_sizes.items())
if sample is not None and len(items) > sample:
random.seed(42)
items = random.sample(items, sample)
print(f"Режим выборки: проверяем {sample} из {len(cache_sizes)} игр")
total = len(items)
suspicious = []
for i, (appid, cached_gb) in enumerate(items):
if cached_gb is None:
continue
if not isinstance(cached_gb, (int, float)):
continue
cached_gb = float(cached_gb)
steamcmd_gb = get_app_size_from_steamcmd(appid)
time.sleep(REQUEST_DELAY)
name = name_by_appid.get(appid, "?")
if i % 20 == 0 or i == total - 1:
print(f"\rПроверено {i + 1}/{total} {name[:40]}", end="", flush=True)
if steamcmd_gb is None:
continue
if cached_gb >= steamcmd_gb * MIN_RATIO:
continue
if steamcmd_gb >= MIN_GB_STEAMCMD and (steamcmd_gb - cached_gb) >= DIFF_GB:
suspicious.append({
"appid": appid,
"name": name,
"cached_gb": round(cached_gb, 2),
"steamcmd_gb": round(steamcmd_gb, 2),
})
print()
print(f"Проверено: {total}")
print(f"Подозрительных (кэш заметно меньше steamcmd): {len(suspicious)}")
if sample and total < len(cache_sizes):
print(f"(оценка на всю библиотеку: ~{len(suspicious) * len(cache_sizes) // max(1, total)} таких игр)")
print()
if suspicious:
print("Список (кэш ГБ → steamcmd ГБ):")
for s in sorted(suspicious, key=lambda x: -x["steamcmd_gb"]):
print(f" {s['appid']:>8} {s['cached_gb']:>7.1f}{s['steamcmd_gb']:>7.1f} {s['name']}")
return suspicious
def heuristic_round_numbers() -> None:
"""Без API: считает записи с «круглым» размером в ГБ (часто из требований магазина)."""
with open(CACHE_FILE, encoding="utf-8") as f:
cache = json.load(f)
if LIBRARY_FILE.exists():
with open(LIBRARY_FILE, encoding="utf-8") as f:
library = json.load(f)
name_by_appid = {g["appid"]: g.get("name", "?") for g in library}
else:
name_by_appid = {}
# круглые числа: целые или X.0, и типичные для магазина (1, 2, 4, 6, 8, 12, 16, 20, 70...)
round_entries = []
for k, v in cache.items():
if v is None:
continue
gb = float(v)
if gb <= 0:
continue
if gb == int(gb) or abs(gb - round(gb)) < 0.01:
round_entries.append((int(k), round(gb, 1), name_by_appid.get(int(k), "?")))
round_entries.sort(key=lambda x: -x[1])
print(f"Записей с «круглым» размером (целое число ГБ): {len(round_entries)} из {len(cache)}")
print("Часто так указывают в системных требованиях магазина, а не реальный размер депо.")
print()
print("Топ по размеру (могут быть занижены):")
for appid, gb, name in round_entries[:30]:
print(f" {gb:>6.1f} ГБ {appid:>8} {name}")
if len(round_entries) > 30:
print(f" ... и ещё {len(round_entries) - 30}")
if __name__ == "__main__":
p = argparse.ArgumentParser(description="Сверка кэша размеров с steamcmd API")
p.add_argument("--sample", type=int, default=None, help="Проверить только N игр (быстрая оценка)")
p.add_argument("--heuristic", action="store_true", help="Только эвристика по круглым числам (без API)")
args = p.parse_args()
if args.heuristic:
heuristic_round_numbers()
else:
main(sample=args.sample)