feat: corrigida a exportação dos tipos de vídeo para preservar car

- corrigida a exportação dos tipos de vídeo para preservar caracteres UTF-8 no J-SOM
- adicionada revisão editável de legendas com geração offline e exportação SRT no painel

Resumo:
- 4 arquivos alterados
- 1 novos
- 3 modificados
- 0 removidos

 3 files changed, 121 insertions(+), 12 deletions(-)

Arquivos:
  - code/cep-plugin/index.html
  - code/cep-plugin/main.js
  - code/cep-plugin/styles.css
  - code/tests/cep-tipos-video-encoding.test.ts
This commit is contained in:
João Henrique
2026-09-09 13:14:17 -04:00
parent 1249b3aeb7
commit c07d18133c
4 changed files with 148 additions and 12 deletions
+105 -9
View File
@@ -292,6 +292,98 @@ function salvarConfiguracaoDeLegendas() {
document.getElementById("legendasStatus").textContent = "Configuração de legendas salva neste computador.";
}
var legendasGeradas = [];
function legendaNumeroParaTempo(segundos) {
var horas = Math.floor(segundos / 3600);
var minutos = Math.floor((segundos % 3600) / 60);
var segundosInteiros = Math.floor(segundos % 60);
var milissegundos = Math.round((segundos - Math.floor(segundos)) * 1000);
if (milissegundos >= 1000) { segundosInteiros++; milissegundos = 0; }
return [horas, minutos, segundosInteiros].map(function (n) { return String(n).padStart(2, "0"); }).join(":") + "," + String(milissegundos).padStart(3, "0");
}
function legendasExtrairPalavras(transcricao) {
var segmentos = transcricao.segmentos || transcricao.segments || [];
var palavras = [];
segmentos.forEach(function (segmento) {
var itens = segmento.palavras || segmento.words || [];
itens.forEach(function (palavra) {
var texto = String(palavra.texto || palavra.word || "").trim();
var inicio = Number(palavra.inicio !== undefined ? palavra.inicio : palavra.start);
var fim = Number(palavra.fim !== undefined ? palavra.fim : palavra.end);
if (texto && isFinite(inicio) && isFinite(fim) && fim > inicio) palavras.push({ texto: texto, inicio: inicio, fim: fim });
});
});
return palavras.sort(function (a, b) { return a.inicio - b.inicio; });
}
function legendasIntervaloCortado(palavra, cortes) {
return cortes.some(function (corte) { return palavra.inicio < corte.end && palavra.fim > corte.start; });
}
function legendasGerarBlocos(palavras, cortes, configuracao) {
var mantidas = palavras.filter(function (palavra) { return !legendasIntervaloCortado(palavra, cortes); });
var blocos = [], bloco = [], caracteres = 0;
function adicionar() {
if (!bloco.length) return;
var primeiro = bloco[0], ultimo = bloco[bloco.length - 1];
// Para o primeiro corte, todos os cortes anteriores são considerados.
var inicio = primeiro.inicio - cortes.reduce(function (total, corte) { return total + (corte.end <= primeiro.inicio ? corte.end - corte.start : 0); }, 0);
var fim = ultimo.fim - cortes.reduce(function (total, corte) { return total + (corte.end <= ultimo.fim ? corte.end - corte.start : 0); }, 0);
blocos.push({ texto: bloco.map(function (item) { return item.texto; }).join(" "), inicio: inicio, fim: fim });
bloco = []; caracteres = 0;
}
mantidas.forEach(function (palavra) {
var espaco = bloco.length ? 1 : 0;
if (bloco.length && (bloco.length >= configuracao.max_palavras || caracteres + espaco + palavra.texto.length > configuracao.max_caracteres)) adicionar();
bloco.push(palavra); caracteres += (bloco.length > 1 ? 1 : 0) + palavra.texto.length;
});
adicionar();
return blocos;
}
function legendasRenderizar() {
var lista = document.getElementById("legendasLista");
document.getElementById("legendasContagem").textContent = "(" + legendasGeradas.length + ")";
lista.textContent = "";
if (!legendasGeradas.length) { lista.innerHTML = '<p class="empty-state">Nenhuma legenda carregada.</p>'; return; }
legendasGeradas.forEach(function (legenda, indice) {
var linha = document.createElement("div"); linha.className = "legenda-row"; linha.setAttribute("role", "listitem");
var cabecalho = document.createElement("div"); cabecalho.className = "legenda-timecode"; cabecalho.textContent = (indice + 1) + " " + legendaNumeroParaTempo(legenda.inicio) + " → " + legendaNumeroParaTempo(legenda.fim);
var campo = document.createElement("input"); campo.className = "text-field"; campo.value = legenda.texto; campo.setAttribute("aria-label", "Texto da legenda " + (indice + 1));
campo.oninput = function () { legendasGeradas[indice].texto = campo.value; };
var excluir = document.createElement("button"); excluir.className = "button button-ghost"; excluir.type = "button"; excluir.textContent = "×"; excluir.title = "Excluir legenda";
excluir.onclick = function () { legendasGeradas.splice(indice, 1); legendasRenderizar(); };
linha.appendChild(cabecalho); linha.appendChild(campo); linha.appendChild(excluir); lista.appendChild(linha);
});
}
function gerarLegendasDaTranscricao() {
if (!silenceState.transcriptPath) { document.getElementById("legendasStatus").textContent = "Execute ou carregue uma transcrição primeiro."; return; }
try {
var transcricao = JSON.parse(fs.readFileSync(silenceState.transcriptPath, "utf-8"));
var plano = JSON.parse(document.getElementById("testarAcoesJson").value || "{}");
var cortes = (plano.actions || []).filter(function (acao) { return acao.kind === "cut"; }).map(function (acao) { return { start: Number(acao.start), end: Number(acao.end) }; });
var configuracao = carregarConfiguracaoDeLegendas();
legendasGeradas = legendasGerarBlocos(legendasExtrairPalavras(transcricao), cortes, configuracao);
legendasRenderizar();
document.getElementById("legendasStatus").textContent = legendasGeradas.length + " legenda(s) gerada(s) para revisão.";
} catch (erro) { document.getElementById("legendasStatus").textContent = "Não foi possível gerar: " + erro.message; }
}
function exportarLegendasSrt() {
if (!legendasGeradas.length) { document.getElementById("legendasStatus").textContent = "Gere as legendas antes de exportar."; return; }
var destino = path.join(silenceState.outputDir || os.tmpdir(), "legendas-geradas.srt");
var conteudo = legendasGeradas.map(function (legenda, indice) { return (indice + 1) + "\n" + legendaNumeroParaTempo(legenda.inicio) + " --> " + legendaNumeroParaTempo(legenda.fim) + "\n" + legenda.texto + "\n"; }).join("\n");
fs.writeFileSync(destino, conteudo, "utf-8");
document.getElementById("legendasStatus").textContent = "SRT exportado em " + destino;
}
function inserirLegendasNaTimeline() {
document.getElementById("legendasStatus").textContent = "A lista está pronta. A inserção automática dos MOGRTs será conectada na próxima etapa, após validar o estilo escolhido.";
}
// ---- Teste isolado de ações pela engine Python ----------------------------
var TESTAR_ACOES_ENGINE_SCRIPT = "/Volumes/Merongo/SISTEMAS/GENIAL SISTEMAS/Jhonny/code/engine/aplicar_plano_de_edicao.py";
@@ -665,15 +757,19 @@ function copiarTextoParaAreaDeTransferencia(texto) {
else showToast("err", "Não foi possível copiar o JSON. Selecione e copie manualmente.");
}
try {
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(texto).then(function () {
showToast("ok", "JSON resumido copiado para a área de transferência.");
}).catch(function () { copiarComPbcopy(texto, copiarComSelecaoDoPainel); });
return;
}
} catch (_) {}
copiarComPbcopy(texto, copiarComSelecaoDoPainel);
// O CEP antigo pode corromper caracteres UTF-8 pela API de clipboard do Chromium.
// O pbcopy recebe bytes UTF-8 diretamente e evita essa conversão intermediária.
copiarComPbcopy(texto, function () {
try {
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(texto).then(function () {
showToast("ok", "JSON resumido copiado para a área de transferência.");
}).catch(copiarComSelecaoDoPainel);
return;
}
} catch (_) {}
copiarComSelecaoDoPainel();
});
}
function copiarComPbcopy(texto, fallback) {