Names check for a copied campus; the ATEM folder says when the page may not read Google Drive

Site setup > Companion lists every connection, Dante device and MIDI session the keys use that
this campus does not report, with a map-to choice that renames it across checks, restart keys
and fixes. The ATEM section explains the Full Disk Access step when the cloud folder looks empty
to the background page. Verified on NA Ingest; Install the staged folder tested live there too.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-10-14 15:09:17 -04:00
co-authored by Claude Fable 5.1
parent a33e344cae
commit 3b3a1c7245
3 changed files with 97 additions and 4 deletions
+56 -2
View File
@@ -644,12 +644,21 @@ class Handler(http.server.BaseHTTPRequestHandler):
return self.send(200, spotify_info(url))
if path == "/api/atem-reference/folder":
folder = os.path.expanduser((site.cfg.get("atem") or {}).get("source_folder") or "")
files = sorted(glob.glob(os.path.join(folder, "*.xml")), key=os.path.getmtime, reverse=True) if folder and os.path.isdir(folder) else []
return self.send(200, {"folder": folder, "mounted": bool(folder and os.path.isdir(folder)),
mounted = bool(folder and os.path.isdir(folder))
files = sorted(glob.glob(os.path.join(folder, "*.xml")), key=os.path.getmtime, reverse=True) if mounted else []
blocked = False
if mounted and not files:
try:
blocked = not os.listdir(folder) and "/Library/CloudStorage/" in folder # a cloud folder that looks empty to a background process
except OSError:
blocked = True
return self.send(200, {"folder": folder, "mounted": mounted, "blocked": blocked,
"files": [{"name": os.path.basename(f), "when": os.path.getmtime(f)} for f in files[:10]]})
if path == "/api/live/midi":
snap = site.read_json("snapshot.json", {}) or {}
return self.send(200, {"local": snap.get("midi"), "remote": snap.get("remote_midi")})
if path == "/api/site/names":
return self.send(200, self.names_check((site.draft.read() or {}).get("config") or site.load()))
if path == "/api/setup":
return self.send(200, {"computers": scripts.setup_all(site.load())})
if path == "/api/program/staged":
@@ -878,6 +887,51 @@ class Handler(http.server.BaseHTTPRequestHandler):
log.exception("%s %s", self.command, path)
return self.send(500, {"error": str(e)})
def names_check(self, cfg):
"""Every device name the keys use, against what this Companion, the Dante network and the Macs report now:
the mapping step of a copied campus."""
site = self.site
try:
live = {c["label"]: c.get("moduleId") for c in site.comp.connections()}
companion_ok = True
except Exception:
live, companion_ok = {}, False
used = {}
for t in cfg.get("tiles", []):
for c in t.get("checks", []):
if c.get("conn") and c["type"] != "dante" or (c["type"] == "dante" and c.get("conn")):
used.setdefault(c["conn"], {"keys": set(), "module": schema.EXPECTED_MODULE.get(c["type"])})["keys"].add(t.get("label", t["id"]))
for k in (cfg.get("restart_page") or {}).get("keys", []):
for w in k.get("watch", []):
if w.startswith("conn:"):
used.setdefault(w[5:], {"keys": set(), "module": None})["keys"].add("restart key " + k.get("label", k["id"]))
conns = []
for name, u in sorted(used.items()):
cands = [l for l, m in live.items() if l not in used and (not u["module"] or not m or m == u["module"])]
conns.append({"name": name, "keys": sorted(u["keys"]), "live": name in live, "module": u["module"],
"live_module": live.get(name), "candidates": sorted(cands)})
snap = site.read_json("snapshot.json", {}) or {}
dmap = snap.get("dante_map") or {}
dante = []
for t in cfg.get("tiles", []):
for c in t.get("checks", []):
if c["type"] == "dante":
for n in c.get("devices", {}):
dante.append({"name": n, "seen": n in dmap, "candidates": sorted(x for x in dmap if x not in c["devices"])})
local, remote = snap.get("midi") or {}, snap.get("remote_midi") or {}
midi = []
for t in cfg.get("tiles", []):
for c in t.get("checks", []):
if c["type"] == "midi_peer":
src = (remote.get(c["host"]) or {}) if c.get("host") else local
sess = src.get(c.get("session")) if isinstance(src, dict) else None
peers = [p.get("name") for p in (sess or {}).get("peers", [])] if sess else []
midi.append({"session": c.get("session"), "peer": c.get("peer"), "host": c.get("host"), "key": t.get("label", t["id"]),
"session_seen": sess is not None, "peer_seen": c.get("peer") in peers,
"sessions_seen": sorted(src) if isinstance(src, dict) else [], "peers_seen": peers})
return {"companion_ok": companion_ok, "connections": conns, "dante": dante, "midi": midi,
"missing": sum(1 for c in conns if not c["live"]) + sum(1 for d in dante if not d["seen"]) + sum(1 for m in midi if not m["session_seen"] or not m["peer_seen"])}
def staged(self):
"""The staged folder (~/status-panel-install) against the running code."""
src = os.path.expanduser("~/status-panel-install")