поправили механику работы шорткатов

This commit is contained in:
2026-08-24 13:37:50 +03:00
parent e09a1eeff5
commit 7d934a6ee6
4 changed files with 259 additions and 34 deletions

View File

@@ -1505,9 +1505,18 @@ class VmixAgentHub:
prepared_commands.append(command)
results: list[dict[str, Any]] = []
transport = "batch" if self._agent_supports_batch(agent_version) and len(prepared_commands) > 1 else "legacy"
# BUILD100: operator Shortcut/Quick-panel traffic is intentionally delivered
# as ordered single vmix.command frames with an ACK for every command. Mapping
# keeps its chunked batch transport and generic non-shortcut runtime sync may
# still use Agent 1.4 vmix.batch. Interactive title/timer actions are small, and
# losing the rest of a shortcut after one bad command is much worse than a few
# extra websocket frames.
interactive_shortcut = bool(str(sequence_id or "").strip() or str(button_id or "").strip())
use_batch = (not interactive_shortcut) and self._agent_supports_batch(agent_version) and len(prepared_commands) > 1
transport = "batch" if use_batch else ("shortcut-sequential" if interactive_shortcut else "legacy")
async with self._device_vmix_send_lock(target_device_id):
if transport == "batch":
if use_batch:
ack = await self._send_vmix_batch_unlocked(
target_device_id,
assignment_id=assignment_id,
@@ -1531,40 +1540,60 @@ class VmixAgentHub:
self._track_runtime_overlay_command(
target_device_id, command, sequence_id=sequence_id, sequence_name=sequence_name, button_id=button_id
)
if not all(item["ok"] for item in results):
failed = next(item for item in results if not item["ok"])
raise HTTPException(
status_code=502,
detail={"message": f"vMix command failed: {failed['function']}", "index": failed["index"], "results": results},
)
else:
for index, command in enumerate(prepared_commands):
function = str(command.get("Function") or "")
ack = await self._send_vmix_command_unlocked(
target_device_id, assignment_id=assignment_id, match_id=match_id, command=command, timeout=timeout
)
item = {
"index": index,
"function": function,
"ok": bool(ack.get("ok")),
"reason": str(ack.get("reason") or ack.get("error") or ""),
}
results.append(item)
if not item["ok"]:
raise HTTPException(
status_code=502,
detail={"message": f"vMix command failed: {function}", "index": index, "results": results},
try:
ack = await self._send_vmix_command_unlocked(
target_device_id,
assignment_id=assignment_id,
match_id=match_id,
command=command,
timeout=timeout,
)
self._track_runtime_overlay_command(
target_device_id, command, sequence_id=sequence_id, sequence_name=sequence_name, button_id=button_id
)
item = {
"index": index,
"function": function,
"ok": bool(ack.get("ok")),
"reason": str(ack.get("reason") or ack.get("error") or ""),
}
except Exception as error:
detail = getattr(error, "detail", None)
if isinstance(detail, dict):
reason = str(detail.get("message") or detail)
else:
reason = str(detail or error or "vmix_command_error")
item = {
"index": index,
"function": function,
"ok": False,
"reason": reason[:500],
}
results.append(item)
if item["ok"]:
self._track_runtime_overlay_command(
target_device_id,
command,
sequence_id=sequence_id,
sequence_name=sequence_name,
button_id=button_id,
)
# For ordinary legacy non-shortcut runtime calls preserve the old
# fail-fast behavior. Shortcut delivery continues deliberately.
if not item["ok"] and not interactive_shortcut:
break
failed = [item for item in results if not item["ok"]]
return {
"ok": True,
"ok": not failed and len(results) == len(prepared_commands),
"device_id": target_device_id,
"match_id": match_id,
"assignment_id": assignment_id,
"session_token": session_token,
"applied": len(results),
"applied": sum(1 for item in results if item["ok"]),
"attempted": len(results),
"requested": len(prepared_commands),
"failed": len(failed),
"results": results,
"transport": transport,
"overlay_state": self._runtime_overlay_payload(target_device_id),