Files
糖糖 d331128e88 输出设备不写死: 面板设备选择 + 自动挂载守护(治开机后"双重混响")
- 问题: 30-collaplex-eq.conf 的 node.target 是安装时探测一次写死的。开机时设备若还没枚举
  (USB 耳机晚到很常见: 09-24 实测开机 08:57、耳机 09:19:43 才出现; 09-23 是 21:01 → 21:03),
  建链时目标不存在 → 末段连到别处或干脆不连, 设备出现后不会自己改回来 → 听感"双重混响",
  只能 stop + start
- 新增 web/output_route.py: 设备清单(pw-dump) + 选择落盘(output.json) + 守护线程(每 3 秒核对
  collaplex_eq_out 只连选中设备, 不对就拆错的、按声道补对的) → 设备一出现自动挂上
- web/server.py: GET /api/devices, POST /api/device(落盘 + 立刻挂载), 快照带 output 段,
  main() 起守护线程
- web/index.html: 「输出设备」卡片(下拉 + 应用 + 状态 + 守护备注; 设备表变化才重建选项, 免得
  60 Hz 重建打断点击)
- 脚本/安装.sh: 优先用记住的设备(不在位也照样按它建链), 不再"猜第一个 digital"
- 实测: 面板真点应用切走再切回 ✓ / 连错 2 秒自动纠回 ✓ / 全拆 3 秒自动挂回 ✓ / start 自检
  打印 [在位] ✓ / 面板像素验收 ✓; pyright strict 0 errors 0 warnings
- 坑: pw-dump 的 Link 键名是 output-port-id / input-port-id(写 output-port 一条连线都读不到);
  面板服务 enable --now 不会重启已在运行的实例(改 web/*.py 必须 restart)
2026-09-24 09:36:36 +08:00

58 lines
2.3 KiB
Python

"""面板截图(CDP): 打开 http://127.0.0.1:8789/ 截图, 用于像素级验收。
用法: uv run --with websocket-client python 面板截图.py [输出png] [宽] [高]
前置: google-chrome --headless=new --disable-gpu --no-sandbox \
--remote-debugging-port=9333 --remote-allow-origins='*' about:blank &
"""
from __future__ import annotations
import base64
import json
import sys
import time
import urllib.request
import websocket
OUT = sys.argv[1] if len(sys.argv) > 1 else "/tmp/panel.png"
W = int(sys.argv[2]) if len(sys.argv) > 2 else 1200
H = int(sys.argv[3]) if len(sys.argv) > 3 else 1400
URL = "http://127.0.0.1:8789/"
targets = json.load(urllib.request.urlopen("http://127.0.0.1:9333/json/list"))
page = [t for t in targets if t["type"] == "page"][0]
ws = websocket.create_connection(page["webSocketDebuggerUrl"], timeout=30)
_seq = [0]
def cmd(method: str, **params: object) -> dict[str, object]:
"""发一条 CDP 命令并等回包。"""
_seq[0] += 1
ws.send(json.dumps({"id": _seq[0], "method": method, "params": params}))
while True:
msg = json.loads(ws.recv())
if msg.get("id") == _seq[0]:
return msg
cmd("Page.enable")
cmd("Emulation.setDeviceMetricsOverride", width=W, height=H,
deviceScaleFactor=1, mobile=False)
cmd("Page.navigate", url=URL)
time.sleep(3.0) # 等页面 + SSE 首帧
# 顺手把新卡片里的文字读出来(和像素一起留证)
expr = ("JSON.stringify({sel:document.getElementById('outdev')?document.getElementById('outdev').options.length:-1,"
"value:document.getElementById('outdev')?document.getElementById('outdev').value:'',"
"state:document.getElementById('outstate')?document.getElementById('outstate').textContent:'',"
"hint:document.getElementById('outhint')?document.getElementById('outhint').textContent:'',"
"status:document.getElementById('status')?document.getElementById('status').textContent:''})")
got = cmd("Runtime.evaluate", expression=expr, returnByValue=True)
print("页面自述:", got.get("result", {}).get("result", {}).get("value"))
shot = cmd("Page.captureScreenshot", format="png", captureBeyondViewport=True)
data = base64.b64decode(str(shot["result"]["data"])) # type: ignore[index]
with open(OUT, "wb") as fh:
fh.write(data)
print("已保存:", OUT, len(data), "字节")