diff --git a/.gitignore b/.gitignore index 173ad59..24cbabf 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,6 @@ android/local.properties # editor leftovers *.swp *~ + +# python leftovers +__pycache__/ diff --git a/RELEASING.md b/RELEASING.md index be3fbef..e749d2d 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -110,10 +110,22 @@ Separate from all of the above, and manual: upload the six files in ## If the screenshots need redoing -`fastlane/metadata/android/en-US/images/phoneScreenshots/` holds five shots at -1170×2532. They are generated from `public_html/index.html` itself with headless -Chromium, not taken on a phone, so they can be regenerated exactly. Two traps if -you do it by hand: +```bash +python3 tools/screenshots.py +``` + +That rewrites all five in +`fastlane/metadata/android/en-US/images/phoneScreenshots/` at 1170×2532. They +are rendered from `public_html/index.html` with headless Chromium, not taken on +a phone, so they reproduce exactly — anything that changes the look of the app +is a reason to re-run it. + +The script sets each scene by seeding `localStorage` before the app boots, so +the app renders its own saved state rather than having the DOM poked from +outside. Editing a scene means editing the `SCENES` table at the top. + +Three traps, all of which fail quietly, and all of which the script already +handles — they are written down here because they cost an afternoon each: - The capture lands whenever the virtual-time budget runs out, not when your script finishes — so freeze the render loop (`requestAnimationFrame = () => 0`) @@ -121,6 +133,10 @@ you do it by hand: - CSS transitions don't advance under `--virtual-time-budget`, so a panel caught mid-transition photographs in its *old* colour. Disable transitions in the screenshot build. +- Snap-confined Chromium cannot write into hidden directories — not `~/.cache`, + not any dot-directory, not the private `/tmp` it is handed. It fails with a + bare "Permission denied" and no hint as to why. Both the HTML it reads and the + PNG it writes have to sit in a plainly-named directory. ## Reference diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png index 4710270..030022a 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png index 9fa02ab..e6c0b56 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png index d0ce49e..a3c2a6e 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png index 59e40ad..31e197c 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png index 4512acd..ea064ec 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png differ diff --git a/public_html/index.html b/public_html/index.html index 33c02db..362cd1e 100644 --- a/public_html/index.html +++ b/public_html/index.html @@ -14,16 +14,18 @@ + +""" + + +def find_chromium(): + for name in CHROMIUM: + path = shutil.which(name) + if path: + return path + sys.exit("no chromium found; tried: " + ", ".join(CHROMIUM)) + + +def build_page(scene, source): + """A copy of the app with the scene seeded and the render loop pinned.""" + seed = { + "bg.points": str(scene["cfg"]["points"]), + "bg.base": str(scene["cfg"]["base"]), + "bg.delay": str(scene["cfg"]["delay"]), + "bg.theme": scene["cfg"].get("theme", "sage"), + "bg.sound": "1", + } + if scene["game"] is not None: + seed["bg.game"] = json.dumps(dict(v=1, **scene["game"])) + + html = source.replace("", SEED % json.dumps(seed) + "", 1) + html = html.replace("", FREEZE % json.dumps(scene["click"]) + "", 1) + return html + + +def main(): + chromium = find_chromium() + source = SOURCE.read_text() + WORK.mkdir(exist_ok=True) + try: + for scene in SCENES: + page = WORK / ("scene-%s.html" % scene["name"]) + page.write_text(build_page(scene, source)) + target = OUT / ("%s.png" % scene["name"]) + shot = WORK / target.name + + subprocess.run([ + chromium, + "--headless", "--no-sandbox", "--disable-gpu", "--hide-scrollbars", + "--window-size=%d,%d" % (WIDTH, HEIGHT), + "--force-device-scale-factor=%d" % SCALE, + "--virtual-time-budget=4000", + "--screenshot=%s" % shot, + page.as_uri(), + ], check=True, capture_output=True) + + if not shot.exists(): + sys.exit("chromium wrote nothing for scene %s" % scene["name"]) + shutil.move(str(shot), str(target)) + print("%s %d bytes" % (target.relative_to(ROOT), target.stat().st_size)) + finally: + shutil.rmtree(WORK, ignore_errors=True) + + +if __name__ == "__main__": + main()