Colour the page nav keys on every page to follow the ALL button.
nav_templates.json holds Companion's own UP / page number / DOWN keys after 'convert to regular button'; make_page.py colours them from the ALL tile and nav_keys.py copies them onto all 96 other pages (288 keys, recorded in nav_locations.json; --revert restores the built-in keys). Also rewrites the README's top half, which an earlier edit had duplicated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+116
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Make the UP / page number / DOWN keys on every page follow the status page's ALL button.
|
||||
|
||||
The status page (page 19) carries coloured copies of Companion's own nav keys
|
||||
(see make_page.py and nav_templates.json). This copies them over the built-in
|
||||
nav keys on every other page, wherever those sit, using the same "copy button"
|
||||
call as the Companion UI. 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 find_nav_keys(host, port, status_page):
|
||||
"""[(page, row, column, kind)] 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))
|
||||
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"]
|
||||
|
||||
record = json.load(open(RECORD)) if os.path.exists(RECORD) else {}
|
||||
if args.revert:
|
||||
targets = [(int(k.split("/")[0]), int(k.split("/")[1]), int(k.split("/")[2]), v) for k, v in record.items()]
|
||||
else:
|
||||
targets = find_nav_keys(args.host, args.port, status_page)
|
||||
print("%d nav keys on %d pages" % (len(targets), len({t[0] for t in targets})))
|
||||
if args.dry_run:
|
||||
for t in targets:
|
||||
print(" page %d row %d col %d: %s" % t)
|
||||
return
|
||||
|
||||
ws = WebSocket(args.host, args.port, "/trpc")
|
||||
done = 0
|
||||
try:
|
||||
api = Trpc(ws)
|
||||
for page, row, col, kind in targets:
|
||||
location = {"pageNumber": page, "row": row, "column": col}
|
||||
if args.revert:
|
||||
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("%s %d nav keys" % ("restored" if args.revert else "coloured", done))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user