diff --git a/.gitignore b/.gitignore index 7f62b1f..0540138 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ logs.txt rgb111-full.png state-new.json settings.json +screenshots/ diff --git a/astley.png b/astley.png new file mode 100644 index 0000000..6c86ea4 Binary files /dev/null and b/astley.png differ diff --git a/async-bot.py b/async-bot.py index 7aea545..884897a 100644 --- a/async-bot.py +++ b/async-bot.py @@ -59,6 +59,7 @@ class AsyncBotManager: bits_on, bits_off, timestamp = data if timestamp < self._last_update: print("SKIPPING UPDATES: TOO OLD") + self._last_update: int = data["timestamp"] for ndx in bits_on: y, x = divmod(ndx, 1000) self.canvas.putpixel((x, y), 255) @@ -107,7 +108,7 @@ async def amain(): mgr.font = ImageFont.truetype(settings["font"], 8) for elem in settings["elements"]: if elem["type"] == "text": - mgr.put_text(elem["text"], elem["x"], elem["y"]) + mgr.put_text(elem["x"], elem["y"], elem["text"]) elif elem["type"] == "image": with Image.open(elem["path"]).convert("LA") as im: mgr.put_image(elem["x"], elem["y"], im) diff --git a/live-viewer.py b/live-viewer.py index 82d1218..18fa447 100644 --- a/live-viewer.py +++ b/live-viewer.py @@ -4,10 +4,12 @@ from PIL import Image, ImageTk from socketio import SimpleClient, exceptions from threading import Thread from requests import get +from datetime import datetime COLORS = [(0x33, 0x33, 0x66), (0x96, 0x96, 0xe0)] COLORS_UNPACKED = COLORS[0] + ((0, 0, 0) * 254) + COLORS[1] +SCREENSHOTS_TMPL = "./screenshots/%Y%m%d_%H%M%S.png" class App(tk.Tk): def __init__(self, url: str = "https://onemillioncheckboxes.com") -> None: @@ -22,6 +24,8 @@ class App(tk.Tk): self._canvas.pack() self.config(width=1000, height=1000, borderwidth=0, highlightthickness=0) + self._last_update = 0 + self._running = False self._image = Image.new("RGB", (1000, 1000), COLORS[0]) @@ -38,15 +42,25 @@ class App(tk.Tk): x, y = event.x, event.y self.sio.emit("toggle_bit", { "index": x + y * 1000 }) + def _save_image(self): + ts = datetime.fromtimestamp(self._last_update / 1000) + path = ts.strftime(SCREENSHOTS_TMPL) + print("SAVED", path) + self._image.save(path) + + self.after(1000, self._save_image) + def _on_key_down(self, event: tk.Event): # if event.keysym == "r": print("FULL REFRESH") with get(f"{self.url}/api/initial-state") as req: - buffer = b64decode(req.json()["full_state"].encode() + b"=") + data = req.json() + buffer = b64decode(data["full_state"].encode() + b"=") img = Image.frombytes("1", (1000, 1000), buffer).convert("P") img.putpalette(COLORS_UNPACKED) self._image.paste(img.convert("RGB")) + self._last_update = data["timestamp"] print("FULL REFRESH DONE") def _reader(self): @@ -59,6 +73,10 @@ class App(tk.Tk): if name == "batched_bit_toggles": bits_on, bits_off, timestamp = data + if timestamp < self._last_update: + print("SKIP partial updates: too old") + continue + self._last_update = timestamp for ndx in bits_on: y, x = divmod(ndx, 1000) self._image.putpixel((x, y), COLORS[1]) @@ -92,7 +110,9 @@ class App(tk.Tk): self.sio.connect(f"{self.url}/socket.io") with get(f"{self.url}/api/initial-state") as req: - buffer = b64decode(req.json()["full_state"].encode() + b"=") + data = req.json() + buffer = b64decode(data["full_state"].encode() + b"=") + self._last_update = data["timestamp"] img = Image.frombytes("1", (1000, 1000), buffer).convert("P") img.putpalette(COLORS_UNPACKED) self._image.paste(img.convert("RGB")) @@ -104,6 +124,7 @@ class App(tk.Tk): self._reader_thr.start() self.protocol("WM_DELETE_WINDOW", self._close) + self.after(1000, self._save_image) self.mainloop() if __name__ == "__main__":