тест 1 времени
This commit is contained in:
@@ -1403,6 +1403,7 @@ class VmixAgentHub:
|
||||
sequence_id: str = "",
|
||||
sequence_name: str = "",
|
||||
button_id: str = "",
|
||||
delivery_mode: str = "",
|
||||
timeout: float = 4.0,
|
||||
) -> dict[str, Any]:
|
||||
"""Execute commands on exactly one Agent bound to this browser/match session."""
|
||||
@@ -1522,49 +1523,92 @@ class VmixAgentHub:
|
||||
# for this transport. Mapping and generic runtime sync keep their existing ACK
|
||||
# semantics so diagnostics are still useful where exact field delivery matters.
|
||||
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 = "shortcut-no-ack" if interactive_shortcut else ("batch" if use_batch else "legacy")
|
||||
delivery_mode = str(delivery_mode or "").strip().lower()
|
||||
fast_timer = delivery_mode == "timer-fast"
|
||||
no_ack_delivery = interactive_shortcut or fast_timer
|
||||
supports_batch = self._agent_supports_batch(agent_version) and len(prepared_commands) > 1
|
||||
use_batch = (not no_ack_delivery) and supports_batch
|
||||
if fast_timer:
|
||||
transport = "timer-batch-no-ack" if supports_batch else "timer-no-ack"
|
||||
else:
|
||||
transport = "shortcut-no-ack" if interactive_shortcut else ("batch" if use_batch else "legacy")
|
||||
|
||||
if interactive_shortcut:
|
||||
# Preserve order between operator shortcuts, but do not wait behind Mapping's
|
||||
# command ACK lock. A shortcut returns as soon as its frames are on the Agent socket.
|
||||
if no_ack_delivery:
|
||||
# BUILD103: realtime operator traffic (shortcuts + timers) bypasses Mapping's
|
||||
# ACK queue. Timer pairs use ONE vmix.batch frame on Agent 1.4+ and return
|
||||
# immediately after WebSocket delivery; the later Agent ACK is intentionally ignored.
|
||||
# The shared realtime lock preserves exact frame order if Space and an F-key are
|
||||
# pressed almost simultaneously without reintroducing the slow Mapping lock.
|
||||
async with self._device_vmix_shortcut_send_lock(target_device_id):
|
||||
for index, command in enumerate(prepared_commands):
|
||||
function = str(command.get("Function") or "")
|
||||
if fast_timer and supports_batch:
|
||||
request_id = secrets.token_urlsafe(12)
|
||||
delivered = await self.send(
|
||||
target_device_id,
|
||||
{
|
||||
"type": "vmix.command",
|
||||
"type": "vmix.batch",
|
||||
"protocol": AGENT_PROTOCOL_VERSION,
|
||||
"request_id": request_id,
|
||||
"device_id": target_device_id,
|
||||
"assignment_id": assignment_id,
|
||||
"match_id": match_id,
|
||||
"command": command,
|
||||
"commands": prepared_commands,
|
||||
},
|
||||
)
|
||||
item = {
|
||||
"index": index,
|
||||
"function": function,
|
||||
"ok": bool(delivered),
|
||||
"reason": "" if delivered else "Agent сейчас offline",
|
||||
"delivery": "agent-websocket",
|
||||
"ack_waited": False,
|
||||
}
|
||||
results.append(item)
|
||||
if delivered:
|
||||
# With no ACK wait the local ON AIR state is intentionally optimistic:
|
||||
# success means delivery to the live Agent WebSocket, not vMix confirmation.
|
||||
self._track_runtime_overlay_command(
|
||||
for index, command in enumerate(prepared_commands):
|
||||
function = str(command.get("Function") or "")
|
||||
results.append({
|
||||
"index": index,
|
||||
"function": function,
|
||||
"ok": bool(delivered),
|
||||
"reason": "" if delivered else "Agent сейчас offline",
|
||||
"delivery": "agent-websocket-batch",
|
||||
"ack_waited": False,
|
||||
})
|
||||
if delivered:
|
||||
self._track_runtime_overlay_command(
|
||||
target_device_id,
|
||||
command,
|
||||
sequence_id=sequence_id,
|
||||
sequence_name=sequence_name,
|
||||
button_id=button_id,
|
||||
)
|
||||
else:
|
||||
for index, command in enumerate(prepared_commands):
|
||||
function = str(command.get("Function") or "")
|
||||
request_id = secrets.token_urlsafe(12)
|
||||
delivered = await self.send(
|
||||
target_device_id,
|
||||
command,
|
||||
sequence_id=sequence_id,
|
||||
sequence_name=sequence_name,
|
||||
button_id=button_id,
|
||||
{
|
||||
"type": "vmix.command",
|
||||
"protocol": AGENT_PROTOCOL_VERSION,
|
||||
"request_id": request_id,
|
||||
"device_id": target_device_id,
|
||||
"assignment_id": assignment_id,
|
||||
"match_id": match_id,
|
||||
"command": command,
|
||||
},
|
||||
)
|
||||
else:
|
||||
break
|
||||
item = {
|
||||
"index": index,
|
||||
"function": function,
|
||||
"ok": bool(delivered),
|
||||
"reason": "" if delivered else "Agent сейчас offline",
|
||||
"delivery": "agent-websocket",
|
||||
"ack_waited": False,
|
||||
}
|
||||
results.append(item)
|
||||
if delivered:
|
||||
# With no ACK wait the local ON AIR state is intentionally optimistic:
|
||||
# success means delivery to the live Agent WebSocket, not vMix confirmation.
|
||||
self._track_runtime_overlay_command(
|
||||
target_device_id,
|
||||
command,
|
||||
sequence_id=sequence_id,
|
||||
sequence_name=sequence_name,
|
||||
button_id=button_id,
|
||||
)
|
||||
else:
|
||||
break
|
||||
else:
|
||||
async with self._device_vmix_send_lock(target_device_id):
|
||||
if use_batch:
|
||||
@@ -1637,7 +1681,8 @@ class VmixAgentHub:
|
||||
"failed": len(failed),
|
||||
"results": results,
|
||||
"transport": transport,
|
||||
"confirmation": "not_waited" if interactive_shortcut else "ack",
|
||||
# BUILD102 compatibility marker: "confirmation": "not_waited" if interactive_shortcut else "ack"
|
||||
"confirmation": "not_waited" if no_ack_delivery else "ack",
|
||||
"overlay_state": self._runtime_overlay_payload(target_device_id),
|
||||
}
|
||||
|
||||
@@ -3914,6 +3959,7 @@ class RuntimeVmixSequencePayload(BaseModel):
|
||||
sequence_id: str = Field(default="", max_length=160)
|
||||
sequence_name: str = Field(default="", max_length=300)
|
||||
button_id: str = Field(default="", max_length=160)
|
||||
delivery_mode: str = Field(default="", max_length=32)
|
||||
|
||||
|
||||
class SelectSessionDevicePayload(BaseModel):
|
||||
@@ -4259,6 +4305,7 @@ def create_hockey_agent_router(
|
||||
sequence_id=payload.sequence_id,
|
||||
sequence_name=payload.sequence_name,
|
||||
button_id=payload.button_id,
|
||||
delivery_mode=payload.delivery_mode,
|
||||
)
|
||||
|
||||
@router.get("/api/hockey/vmix/overlay-state")
|
||||
|
||||
Reference in New Issue
Block a user