feat: criada a aba Análises no painel CEP para ler do banco (anali
- criada a aba Análises no painel CEP para ler do banco (analises.db) e copiar/abrir o J-SOM de som (transcrição por cena) e o de imagem (evidências visuais), com novo módulo engine/gerar_relatorio_de_analises.py e testes - criado o módulo engine/editor (leitura do plano de edição em JSON, tradução de tempos de origem para a timeline ativa e aplicação de cortes/zooms/marcadores no Premiere via MCP) e o ponto de entrada engine/aplicar_plano_de_edicao.py, com testes; corrigido engine/integracoes/premiere/cliente_mcp.py para levantar os erros específicos já declarados em erros_mcp.py em vez de RuntimeError genérico Resumo: - 15 arquivos alterados - 8 novos - 5 modificados - 2 removidos 7 files changed, 172 insertions(+), 8 deletions(-) Arquivos: - .jhonny/analises.db - .jhonny/analises.db-shm - .jhonny/analises.db-wal - code/cep-plugin/index.html - code/cep-plugin/main.js - code/cep-plugin/styles.css - code/engine/integracoes/premiere/cliente_mcp.py - code/engine/aplicar_plano_de_edicao.py - code/engine/editor/ - code/engine/gerar_relatorio_de_analises.py - code/engine/testes/duplos_de_premiere.py - code/engine/testes/test_aplicador_de_plano_de_edicao.py - code/engine/testes/test_gerar_relatorio_de_analises.py - code/engine/testes/test_leitor_de_plano_de_edicao.py - code/engine/testes/test_mapeador_de_tempo.py
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
"""Modelo de domínio do plano de edição.
|
||||
|
||||
Representa, em memória, o contrato JSON produzido pelas skills de seleção de
|
||||
trechos (``kind``/``start``/``end``/``params``/``reason``) já validado e
|
||||
convertido para tipos internos. Nenhuma classe aqui conhece o Premiere nem o
|
||||
formato de arquivo — isso é responsabilidade de ``leitura`` e ``escrita``.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from typing import Any
|
||||
|
||||
|
||||
class TipoDeAcao(str, Enum):
|
||||
"""Os quatro tipos de ação que o plano de edição pode descrever."""
|
||||
|
||||
CORTE = "cut"
|
||||
ZOOM = "zoom"
|
||||
TEXTO = "text"
|
||||
MARCADOR = "marker"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AcaoDeEdicao:
|
||||
"""Uma decisão de edição sobre um intervalo do arquivo de origem.
|
||||
|
||||
``inicio`` e ``fim`` são sempre segundos na mídia original — nunca na
|
||||
timeline já cortada. ``motivo`` é obrigatório: é o texto que o editor lê
|
||||
para decidir se aceita a decisão.
|
||||
"""
|
||||
|
||||
tipo: TipoDeAcao
|
||||
inicio: float
|
||||
fim: float
|
||||
motivo: str
|
||||
parametros: dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if self.inicio < 0:
|
||||
raise ValueError(f"Ação inválida: início {self.inicio}s não pode ser negativo.")
|
||||
if self.fim <= self.inicio:
|
||||
raise ValueError(
|
||||
f"Ação inválida: fim ({self.fim}s) deve ser maior que início ({self.inicio}s)."
|
||||
)
|
||||
if not self.motivo.strip():
|
||||
raise ValueError("Ação inválida: motivo não pode ser vazio.")
|
||||
|
||||
@property
|
||||
def duracao(self) -> float:
|
||||
"""Duração da ação em segundos, sempre positiva."""
|
||||
return self.fim - self.inicio
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PlanoDeEdicao:
|
||||
"""Conjunto ordenado de ações a aplicar sobre um arquivo de origem."""
|
||||
|
||||
arquivo_de_origem: str
|
||||
acoes: tuple[AcaoDeEdicao, ...]
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if not self.arquivo_de_origem.strip():
|
||||
raise ValueError("Plano de edição inválido: arquivo de origem não informado.")
|
||||
|
||||
def acoes_do_tipo(self, tipo: TipoDeAcao) -> tuple[AcaoDeEdicao, ...]:
|
||||
"""Devolve, na ordem original, só as ações do tipo informado."""
|
||||
return tuple(acao for acao in self.acoes if acao.tipo is tipo)
|
||||
Reference in New Issue
Block a user