"""Espaçamento das chamadas ao bridge do Premiere. As ferramentas de edição do Premiere são round-trips síncronos de QE DOM/ExtendScript no processo principal do aplicativo. Uma rajada dessas chamadas sem folga entre elas trava a interface do Premiere — observado na prática ao aplicar um plano com 17 cortes seguidos de zooms e marcadores. O script ``scripts/apply-editorial-actions.mjs``, que aplica planos por este mesmo bridge sem travar, resolve isso com uma pausa fixa entre chamadas; este módulo traz a mesma folga para o caminho Python. """ from __future__ import annotations import time from typing import Any, Callable from .cliente_mcp import ClienteMCP #: Folga adotada entre chamadas consecutivas, igual à do apply-editorial-actions.mjs #: (``BRIDGE_DELAY_MS = 120``), que é o valor validado numa aplicação real. PAUSA_PADRAO_ENTRE_CHAMADAS_EM_SEGUNDOS = 0.12 class ClienteMCPComPausa(ClienteMCP): """Envolve outro :class:`ClienteMCP` garantindo uma folga entre chamadas. Decora um cliente já pronto em vez de espalhar esperas pelas classes de domínio: assim a folga vale tanto para as leituras (``get_active_sequence``) quanto para as escritas (``split_clip``, ``ripple_delete``), que é o que de fato ocupa o processo principal do Premiere, e as classes de edição continuam sem saber que existe um bridge lento do outro lado. A pausa só entra *entre* chamadas — a primeira não espera — e vale também depois de uma chamada que falhou, porque uma chamada recusada ocupou o bridge do mesmo jeito. """ def __init__( self, cliente_mcp: ClienteMCP, pausa_em_segundos: float = PAUSA_PADRAO_ENTRE_CHAMADAS_EM_SEGUNDOS, esperar: Callable[[float], None] = time.sleep, ) -> None: """``esperar`` é injetável para que os testes não durmam de verdade.""" if pausa_em_segundos < 0: raise ValueError( f"A pausa entre chamadas não pode ser negativa (recebido: {pausa_em_segundos})." ) self.cliente_mcp = cliente_mcp self.pausa_em_segundos = pausa_em_segundos self.esperar = esperar self._ja_chamou = False def conectar(self) -> None: """Delega ao cliente interno; conectar não é um round-trip de edição.""" self.cliente_mcp.conectar() def desconectar(self) -> None: """Delega ao cliente interno; desconectar não é um round-trip de edição.""" self.cliente_mcp.desconectar() def esta_conectado(self) -> bool: """Estado de conexão do cliente interno.""" return self.cliente_mcp.esta_conectado() def chamar(self, nome_da_ferramenta: str, argumentos: dict[str, Any] | None = None) -> dict[str, Any]: """Espera a folga devida (menos na primeira chamada) e delega ao cliente interno.""" if self._ja_chamou and self.pausa_em_segundos > 0: self.esperar(self.pausa_em_segundos) self._ja_chamou = True return self.cliente_mcp.chamar(nome_da_ferramenta, argumentos)