feat: alinhada a remoção da engine Python ao fluxo ripple do execu

- alinhada a remoção da engine Python ao fluxo ripple do executor CEP para cortar vídeo e áudio
- criada remoção atômica para manter vídeo e áudio vinculados sincronizados
- criada aba de configuração de legendas offline e inclusão das preferências no pacote para a IA
- criado gerador offline de blocos de legenda sincronizados com palavras da transcrição e cortes aprovados
- criada uma versão base portátil da skill edit-video-by-voice para testes em outros agentes de IA
- incorporado perfil editorial opcional à skill portátil para orientar decisões de edição

Resumo:
- 14 arquivos alterados
- 4 novos
- 10 modificados
- 0 removidos

 10 files changed, 278 insertions(+), 33 deletions(-)

Arquivos:
  - code/cep-plugin/index.html
  - code/cep-plugin/main.js
  - code/docs/supported-actions.md
  - code/engine/editor/aplicador_de_plano_de_edicao.py
  - code/engine/editor/escrita/escrita_no_editor.py
  - code/engine/testes/duplos_de_premiere.py
  - code/engine/testes/test_aplicador_de_plano_de_edicao.py
  - code/src/tools/timeline.ts
  - code/tests/tools/structural-verification.test.ts
  - code/tests/tools/tool-modules.test.ts
  - code/engine/arquitetura/legendas.md
  - code/engine/editor/legendas/
  - code/engine/testes/test_gerador_de_legendas.py
  - skills/
This commit is contained in:
João Henrique
2026-09-09 13:05:07 -04:00
parent f3e356340b
commit 1249b3aeb7
18 changed files with 730 additions and 33 deletions
+27
View File
@@ -49,6 +49,9 @@
<button class="tab-button" id="tabBtnTestarAcoes" role="tab" aria-selected="false" aria-controls="tabTestarAcoes" onclick="switchTab('testar-acoes')" type="button">
<span class="tab-icon" aria-hidden="true">⚗</span>Testar ações
</button>
<button class="tab-button" id="tabBtnLegendas" role="tab" aria-selected="false" aria-controls="tabLegendas" onclick="switchTab('legendas')" type="button">
<span class="tab-icon" aria-hidden="true">▤</span>Legendas
</button>
</nav>
<!-- O painel é reposicionado para a aba que iniciou a operação. -->
@@ -607,6 +610,30 @@
</details>
</div>
<!-- =============================== LEGENDAS =============================== -->
<div class="tab-panel" id="tabLegendas" role="tabpanel" aria-labelledby="tabBtnLegendas" hidden>
<p class="tab-intro">Configure como as legendas serão geradas automaticamente a partir da transcrição já existente.</p>
<section class="card">
<div class="card-head"><h2>Geração automática</h2><span class="step-chip">Offline</span></div>
<label class="checkbox-field"><input id="legendasHabilitadas" type="checkbox"><span>Gerar legendas quando o plano for aplicado</span></label>
<p class="field-help">A transcrição fornece o texto e os tempos. A IA decide o plano editorial; o painel gera as legendas localmente somente nos trechos mantidos.</p>
</section>
<section class="card">
<div class="card-head"><h2>Estilo e sincronização</h2></div>
<div class="form-grid form-grid-2">
<div><label class="field-label" for="legendasEstilo">Estilo visual</label><select class="select-field" id="legendasEstilo"><option value="word_top_down">Palavras animadas (MOGRT)</option><option value="subtitle_clean">Legenda limpa</option></select></div>
<div><label class="field-label" for="legendasFaixa">Faixa de vídeo</label><input class="text-field" id="legendasFaixa" type="number" min="0" step="1" value="3"></div>
<div><label class="field-label" for="legendasMaxPalavras">Máximo de palavras</label><input class="text-field" id="legendasMaxPalavras" type="number" min="1" max="12" step="1" value="5"></div>
<div><label class="field-label" for="legendasMaxCaracteres">Máximo de caracteres</label><input class="text-field" id="legendasMaxCaracteres" type="number" min="10" max="80" step="1" value="32"></div>
</div>
<label class="field-label" for="legendasMogrtPath">Arquivo MOGRT</label>
<input class="text-field" id="legendasMogrtPath" type="text" spellcheck="false" placeholder="/caminho/para/legenda.mogrt">
<p class="field-help">A fonte e a animação pertencem ao próprio template MOGRT.</p>
<button class="button button-primary" id="btnSalvarLegendas" onclick="salvarConfiguracaoDeLegendas()" type="button">Salvar configuração</button>
<p class="field-help" id="legendasStatus" aria-live="polite"></p>
</section>
</div>
<!-- =============================== AJUSTES =============================== -->
<div class="tab-panel" id="tabSettings" role="tabpanel" aria-labelledby="tabBtnSettings" hidden>
+41
View File
@@ -234,6 +234,7 @@ function switchTab(name) {
{ key: "models", panel: "tabModels", btn: "tabBtnModels" },
{ key: "settings", panel: "tabSettings", btn: "tabBtnSettings" },
{ key: "testar-acoes", panel: "tabTestarAcoes", btn: "tabBtnTestarAcoes" },
{ key: "legendas", panel: "tabLegendas", btn: "tabBtnLegendas" },
];
tabs.forEach(function (t) {
var active = t.key === name;
@@ -253,6 +254,44 @@ function switchTab(name) {
if (name === "tipos-video") tiposVideoAoAbrir();
}
var LEGENDAS_CONFIGURACAO_CHAVE = "schedule_light_configuracao_legendas";
function configuracaoDeLegendasPadrao() {
return { habilitadas: false, estilo: "word_top_down", faixa_video: 3, max_palavras: 5, max_caracteres: 32, mogrt_path: "" };
}
function carregarConfiguracaoDeLegendas() {
var configuracao = configuracaoDeLegendasPadrao();
try {
var salva = JSON.parse(localStorage.getItem(LEGENDAS_CONFIGURACAO_CHAVE) || "null");
if (salva && typeof salva === "object") configuracao = Object.assign(configuracao, salva);
} catch (e) {}
return configuracao;
}
function restaurarConfiguracaoDeLegendas() {
var configuracao = carregarConfiguracaoDeLegendas();
document.getElementById("legendasHabilitadas").checked = configuracao.habilitadas;
document.getElementById("legendasEstilo").value = configuracao.estilo;
document.getElementById("legendasFaixa").value = configuracao.faixa_video;
document.getElementById("legendasMaxPalavras").value = configuracao.max_palavras;
document.getElementById("legendasMaxCaracteres").value = configuracao.max_caracteres;
document.getElementById("legendasMogrtPath").value = configuracao.mogrt_path;
}
function salvarConfiguracaoDeLegendas() {
var configuracao = {
habilitadas: !!document.getElementById("legendasHabilitadas").checked,
estilo: document.getElementById("legendasEstilo").value,
faixa_video: Number(document.getElementById("legendasFaixa").value),
max_palavras: Number(document.getElementById("legendasMaxPalavras").value),
max_caracteres: Number(document.getElementById("legendasMaxCaracteres").value),
mogrt_path: document.getElementById("legendasMogrtPath").value.trim(),
};
try { localStorage.setItem(LEGENDAS_CONFIGURACAO_CHAVE, JSON.stringify(configuracao)); } catch (e) {}
document.getElementById("legendasStatus").textContent = "Configuração de legendas salva neste computador.";
}
// ---- 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";
@@ -2530,6 +2569,7 @@ function silenceGenerateJson() {
};
transcript.settings = Object.assign({}, transcript.settings, {
silence_removal: { enabled: silenceEnabled, min_silence_seconds: minSilence },
legendas: carregarConfiguracaoDeLegendas(),
});
transcript.speaker_configuration = speakerConfigurationForJson();
@@ -3296,6 +3336,7 @@ function downloadModel(model, btn, approxMB) {
syncTranscribeModelSelect();
syncLanguageInfo();
renderEditorPersonalities();
restaurarConfiguracaoDeLegendas();
// Passo 1 é o único liberado até o vídeo ser detectado.
setStep(1, "ready", "Comece aqui");