#!/usr/bin/env python3 """Make the page nav keys on every page follow the status page's ALL button. Which keys follow it is "nav_keys" in config.json (now just the page number). The status page (page 19) carries coloured copies of Companion's own nav keys (see make_page.py and nav_templates.json). This copies those over the built-in keys on every other page, using the same "copy button" call as the Companion UI, and puts back the built-in key for any kind no longer listed. Each key keeps its normal press action. python3 nav_keys.py --dry-run list what would change python3 nav_keys.py apply (also refreshes keys already converted) python3 nav_keys.py --revert put Companion's built-in nav keys back everywhere Only nav keys are touched. Every location changed is recorded in nav_locations.json. """ import argparse import gzip import json import os import urllib.error import urllib.request from make_page import NAV_LOCATIONS, NAV_MARKER from push_page import Trpc, WebSocket HERE = os.path.dirname(os.path.abspath(__file__)) RECORD = os.path.join(HERE, "nav_locations.json") NAV_TYPES = ("pageup", "pagenum", "pagedown") def page_export(host, port, page): url = "http://%s:%d/int/export/page/%d?includeSecrets=false" % (host, port, page) try: with urllib.request.urlopen(url, timeout=10) as r: raw = r.read() except urllib.error.HTTPError: return None try: raw = gzip.decompress(raw) except OSError: pass return json.loads(raw) def nav_kind(control): if control.get("type") in NAV_TYPES: return control["type"] notes = control.get("options", {}).get("notes", "") or "" if control.get("type") == "button-layered" and notes.startswith(NAV_MARKER): for kind in NAV_TYPES: if "(%s)" % kind in notes: return kind return None def is_ours(control): return (control.get("options", {}).get("notes", "") or "").startswith(NAV_MARKER) def find_nav_keys(host, port, status_page): """[(page, row, column, kind, ours)] for every nav key outside the status page.""" found, page = [], 1 while True: export = page_export(host, port, page) if export is None: break if page != status_page: for r, cols in export["page"]["controls"].items(): for c, control in cols.items(): kind = nav_kind(control or {}) if kind: found.append((page, int(r), int(c), kind, is_ours(control))) page += 1 return found def main(): cfg = json.load(open(os.path.join(HERE, "config.json"))) ap = argparse.ArgumentParser(description=__doc__.splitlines()[0]) ap.add_argument("--host", default="na-ingest.local") ap.add_argument("--port", type=int, default=8000) ap.add_argument("--dry-run", action="store_true") ap.add_argument("--revert", action="store_true") args = ap.parse_args() status_page = cfg["page"] coloured = cfg.get("nav_keys", list(NAV_LOCATIONS)) record = json.load(open(RECORD)) if os.path.exists(RECORD) else {} found = find_nav_keys(args.host, args.port, status_page) if args.revert: work = [(p, r, c, k, "restore") for p, r, c, k, ours in found if ours] else: work = [(p, r, c, k, "colour") for p, r, c, k, ours in found if k in coloured] work += [(p, r, c, k, "restore") for p, r, c, k, ours in found if ours and k not in coloured] print("%d nav keys to colour, %d to restore, on %d pages" % ( sum(1 for w in work if w[4] == "colour"), sum(1 for w in work if w[4] == "restore"), len({w[0] for w in work}))) if args.dry_run: for w in work: print(" page %d row %d col %d: %s -> %s" % w) return ws = WebSocket(args.host, args.port, "/trpc") done = 0 try: api = Trpc(ws) for page, row, col, kind, what in work: location = {"pageNumber": page, "row": row, "column": col} if what == "restore": api.mutation("controls.resetControl", {"location": location, "newType": kind}) record.pop("%d/%d/%d" % (page, row, col), None) else: r, c = NAV_LOCATIONS[kind] source = {"pageNumber": status_page, "row": int(r), "column": int(c)} if not api.mutation("controls.copyControl", {"fromLocation": source, "toLocation": location}): raise RuntimeError("copy to page %d row %d col %d failed" % (page, row, col)) record["%d/%d/%d" % (page, row, col)] = kind done += 1 finally: ws.close() json.dump(record, open(RECORD, "w"), indent=1, sort_keys=True) print("done: %d nav keys changed" % done) if __name__ == "__main__": main()