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:
João Henrique
2026-09-08 19:35:30 -04:00
parent 400c3c93dd
commit 4308c72441
24 changed files with 1591 additions and 8 deletions
+93
View File
@@ -206,6 +206,7 @@ function switchTab(name) {
{ key: "silence", panel: "tabSilence", btn: "tabBtnSilence" },
{ key: "scanner", panel: "tabScanner", btn: "tabBtnScanner" },
{ key: "retakes", panel: "tabRetakes", btn: "tabBtnRetakes" },
{ key: "analises", panel: "tabAnalises", btn: "tabBtnAnalises" },
{ key: "bridge", panel: "tabBridge", btn: "tabBtnBridge" },
{ key: "models", panel: "tabModels", btn: "tabBtnModels" },
{ key: "settings", panel: "tabSettings", btn: "tabBtnSettings" },
@@ -224,6 +225,98 @@ function switchTab(name) {
if (name === "settings") renderEditorPersonalities();
if (name === "scanner") { renderScannerTranscriptionOptions(); loadScannerProfile(); renderScannerHuggingFaceStatus(); }
if (name === "retakes") retakesAoAbrir();
if (name === "analises") analisesAoAbrir();
}
// ---- Análises ---------------------------------------------------------------
// Aba que lê o banco de análises (.jhonny/analises.db) já produzido pelo
// Scanner e mostra/copiar os dois J-SOM: som (transcrição por cena) e
// imagem (evidências visuais). A leitura acontece na engine, via script
// dedicado; a aba nunca escreve no banco.
var ANALISES_ENGINE_SCRIPT = "/Volumes/Merongo/SISTEMAS/GENIAL SISTEMAS/Jhonny/code/engine/gerar_relatorio_de_analises.py";
var ANALISES_DB_PATH = "/Volumes/Merongo/SISTEMAS/GENIAL SISTEMAS/Jhonny/.jhonny/analises.db";
var analisesRelatorioPath = null;
function analisesAoAbrir() {
document.getElementById("analisesCaminho").textContent = ANALISES_DB_PATH;
}
function analisesFormatarTempo(segundos) {
var s = Number(segundos) || 0;
var min = Math.floor(s / 60), resto = (s - min * 60).toFixed(1);
return min + ":" + (resto < 10 ? "0" : "") + resto;
}
function analisesCarregar() {
var stamp = new Date().toISOString().replace(/[:.]/g, "-");
analisesRelatorioPath = path.join(os.tmpdir(), "jhonny-analises-" + stamp + ".json");
setBusy("btnAnalisesCarregar", true); taskStart("Análises", "Lendo o banco de análises", null);
var child = childProcess.spawn(SCANNER_PYTHON_BIN, [ANALISES_ENGINE_SCRIPT, ANALISES_DB_PATH, analisesRelatorioPath],
{ cwd: "/Volumes/Merongo/SISTEMAS/GENIAL SISTEMAS/Jhonny/code", stdio: ["ignore", "pipe", "pipe"] });
var stderr = "", stdout = "";
child.stdout.on("data", function (chunk) { stdout += chunk.toString(); });
child.stderr.on("data", function (chunk) { stderr += chunk.toString(); });
child.on("error", function (error) { setBusy("btnAnalisesCarregar", false); taskEnd(false, "Análises indisponíveis", error.stack || error.message); });
child.on("close", function (code) {
setBusy("btnAnalisesCarregar", false);
if (code !== 0 || !fs.existsSync(analisesRelatorioPath)) {
taskEnd(false, "Leitura do banco falhou", stderr || stdout || "Não foi possível gerar o relatório.");
return;
}
taskEnd(true, "Relatório carregado do banco");
analisesRender(JSON.parse(fs.readFileSync(analisesRelatorioPath, "utf-8")));
});
}
function analisesRender(relatorio) {
var video = relatorio.video || {};
var cenas = relatorio.cenas || [];
var falas = cenas.reduce(function (n, c) { return n + (c.falas || []).length; }, 0);
var evidencias = cenas.reduce(function (n, c) {
var resumo = c.resumo_de_evidencias || {};
return n + Object.keys(resumo).reduce(function (m, k) { return m + resumo[k]; }, 0);
}, 0);
document.getElementById("analisesStatus").textContent = "Carregado";
document.getElementById("analisesVideoNome").textContent = video.nome || "(sem nome)";
document.getElementById("analisesResumo").textContent =
cenas.length + " cenas · " + falas + " falas · " + evidencias + " evidências visuais";
document.getElementById("analisesResumoCard").hidden = false;
document.getElementById("btnAnalisesAbrir").disabled = false;
document.getElementById("btnAnalisesCopiar").disabled = false;
var lista = document.getElementById("analisesCenas");
lista.innerHTML = "";
cenas.forEach(function (cena) {
var bloco = document.createElement("div");
bloco.className = "analise-cena";
bloco.setAttribute("role", "listitem");
var titulo = cena.cena_registrada ? "" : " (sem cena gravada)";
var tipos = Object.keys(cena.resumo_de_evidencias || {})
.sort().map(function (k) { return k + " " + cena.resumo_de_evidencias[k]; }).join(", ") || "—";
var falasHtml = (cena.falas || []).map(function (fala) {
return "<p class=\"analise-fala\"><strong>" + (fala.emocao || "—") + "</strong> " +
analisesFormatarTempo(fala.inicio) + "–" + analisesFormatarTempo(fala.fim) + " · " +
String(fala.texto || "").replace(/&/g, "&amp;").replace(/</g, "&lt;") + "</p>";
}).join("") || "<p class=\"analise-fala analise-vazia\">Sem fala neste clipe.</p>";
bloco.innerHTML =
"<div class=\"analise-cena-cab\"><strong>" + cena.clipe_id + titulo + "</strong>" +
"<span>" + analisesFormatarTempo(cena.inicio) + "–" + analisesFormatarTempo(cena.fim) + "</span></div>" +
falasHtml + "<p class=\"analise-evidencias\">Imagem: " + tipos + "</p>";
lista.appendChild(bloco);
});
}
function analisesAbrirJson() {
if (!analisesRelatorioPath) return;
childProcess.spawn("open", [analisesRelatorioPath], { detached: true, stdio: "ignore" }).unref();
}
function analisesCopiar() {
if (!analisesRelatorioPath) return;
try {
var copy = childProcess.spawn("pbcopy");
copy.stdin.end(fs.readFileSync(analisesRelatorioPath, "utf-8"));
showToast("ok", "J-SOM de som e imagem copiados (JSON completo).");
} catch (error) { showToast("err", error.message); }
}
// ---- Retakes ---------------------------------------------------------------