gates / gates (push) Successful in 13m17s
Every catalog app installed, used, backed up, removed keeping data, reinstalled, restored and removed with data on a fresh box from the public ISO (demo-hp VM 9360, Tester4), endpoint-level. Worst: a reinstall's capture rewrites the kept unit so its restore breaks the app (R-1269, P2), the restore still reports success (R-1270, P2), and „use my kept data" loaded another box's future-dated database (R-1271, P2). Drill catalog fixes for the morning list (n8n usage data; OpenGist 256M, proven by a cgroup probe). VM 9360 and LXC 9361 destroyed; hub host Tester4-aa9466 left for the operator. REPORT-catalog-sweep-2026-10-24.md.
68 lines
3.6 KiB
Python
Executable File
68 lines
3.6 KiB
Python
Executable File
#!/opt/pw/bin/python
|
|
"""shot.py — open a page in real Chromium (Playwright) and screenshot it, phone and computer, HU and EN.
|
|
|
|
Rehearsal 2026-10-20, A4. Runs in the throwaway LXC rehearsal1020-browser on demo-hp.
|
|
Chromium is Debian's package (pinned by the image: `chromium --version` is in A4-browser/versions.txt);
|
|
Playwright 1.52.0 from PyPI drives it via executable_path.
|
|
|
|
usage: shot.py URL OUTPREFIX [--sizes phone,desktop] [--langs hu,en] [--state FILE] [--host-header H]
|
|
[--url-env NAME] [--wait MS] [--full]
|
|
--url-env NAME read the URL (or its secret tail) from env NAME and append it to URL — so a capability
|
|
link never appears on a command line or in this script's output.
|
|
--state FILE Playwright storage_state JSON (cookies) to load — a logged-in session.
|
|
Writes OUTPREFIX-<size>-<lang>.png and prints one line per shot: status, final path (secret tails masked), title.
|
|
"""
|
|
import argparse, os, re, sys
|
|
from playwright.sync_api import sync_playwright
|
|
|
|
SIZES = {"phone": dict(width=390, height=844, is_mobile=True, device_scale_factor=2, has_touch=True),
|
|
"desktop": dict(width=1366, height=768, is_mobile=False, device_scale_factor=1, has_touch=False)}
|
|
LANGS = {"hu": "hu-HU", "en": "en-US"}
|
|
UA_PHONE = ("Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) "
|
|
"Chrome/155.0.0.0 Mobile Safari/537.36")
|
|
|
|
|
|
def mask(u: str) -> str:
|
|
return re.sub(r"[0-9a-f]{32,}", "<token>", u)
|
|
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument("url"); ap.add_argument("out")
|
|
ap.add_argument("--sizes", default="phone,desktop"); ap.add_argument("--langs", default="hu,en")
|
|
ap.add_argument("--state"); ap.add_argument("--url-env"); ap.add_argument("--wait", type=int, default=1500)
|
|
ap.add_argument("--full", action="store_true"); ap.add_argument("--ignore-https-errors", action="store_true")
|
|
a = ap.parse_args()
|
|
url = a.url + (os.environ[a.url_env] if a.url_env else "")
|
|
with sync_playwright() as p:
|
|
b = p.chromium.launch(executable_path="/usr/bin/chromium", args=["--no-sandbox"])
|
|
for s in a.sizes.split(","):
|
|
for l in a.langs.split(","):
|
|
kw = dict(viewport={"width": SIZES[s]["width"], "height": SIZES[s]["height"]},
|
|
device_scale_factor=SIZES[s]["device_scale_factor"], is_mobile=SIZES[s]["is_mobile"],
|
|
has_touch=SIZES[s]["has_touch"], locale=LANGS[l],
|
|
extra_http_headers={"Accept-Language": LANGS[l] + "," + l + ";q=0.9"},
|
|
ignore_https_errors=a.ignore_https_errors, timezone_id="Europe/Budapest")
|
|
if s == "phone":
|
|
kw["user_agent"] = UA_PHONE
|
|
if a.state:
|
|
kw["storage_state"] = a.state
|
|
ctx = b.new_context(**kw)
|
|
pg = ctx.new_page()
|
|
try:
|
|
r = pg.goto(url, wait_until="networkidle", timeout=45000)
|
|
st = r.status if r else "-"
|
|
except Exception as e: # noqa: BLE001 — a failed load is a result, recorded
|
|
st = "ERR " + type(e).__name__
|
|
pg.wait_for_timeout(a.wait)
|
|
out = f"{a.out}-{s}-{l}.png"
|
|
pg.screenshot(path=out, full_page=a.full)
|
|
hw = pg.evaluate("document.documentElement.scrollWidth > window.innerWidth")
|
|
print(f"{st}\t{s}\t{l}\t{mask(pg.url)}\t{pg.title()!r}\thscroll={hw}\t{out}")
|
|
ctx.close()
|
|
b.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|