feat: copiada a skill editar-por-voz e seus critérios para as skil
- copiada a skill editar-por-voz e seus critérios para as skills do projeto - adaptada a skill editar-por-voz para usar o banco SQLite como fonte oficial - corrigida a cópia do JSON do tipo de vídeo para priorizar clipboard Unicode - criado carregador único do contexto de edição por voz a partir do SQLite - criada entrada para registrar planos no SQLite e reaproveitar o mesmo plano na aplicação - habilitado carregamento do plano editorial diretamente do SQLite na interface - corrigido o bloqueio visual da etapa de carregamento do plano salvo - configurado carregamento automático do plano salvo após selecionar a sequência - documentada a sequência como raiz de retomada do fluxo de edição - criada a estrutura local para análise de músicas instrumentais com Essentia Resumo: - 22 arquivos alterados - 11 novos - 11 modificados - 0 removidos 11 files changed, 270 insertions(+), 34 deletions(-) Arquivos: - .jhonny/analises.db - CONTEXT.md - code/cep-plugin/index.html - code/cep-plugin/main.js - code/engine/README.md - code/engine/aplicar_plano_de_edicao.py - code/engine/integracoes/audio/__init__.py - code/engine/persistencia/consultas.py - code/engine/requirements-audio.txt - code/engine/testes/test_consultas_de_persistencia.py - code/tests/cep-tipos-video-encoding.test.ts - code/.jhonny/ - code/engine/carregar_plano_de_edicao.py - code/engine/integracoes/audio/contratos.py - code/engine/integracoes/audio/modelos_de_analise_musical.py - code/engine/integracoes/audio/provider_de_analise_musical_essentia.py - code/engine/preparar_edicao_por_voz.py - code/engine/registrar_plano_de_edicao.py - code/engine/testes/test_analisador_de_musica_essentia.py - code/engine/testes/test_carregar_plano_de_edicao.py - code/engine/testes/test_registrar_plano_de_edicao.py - code/plugins/premiere-pro/skills/editar-por-voz/
This commit is contained in:
+67
-19
@@ -479,7 +479,7 @@ function testarAcoesExecutar() {
|
||||
SCANNER_PYTHON_BIN,
|
||||
[TESTAR_ACOES_ENGINE_SCRIPT, entrada],
|
||||
function (linha) { testarAcoesLog(linha); },
|
||||
function (codigo, ultimoErro) {
|
||||
function (codigo, ultimoErro, ultimaSaida) {
|
||||
if (botao) botao.disabled = false;
|
||||
if (codigo !== 0) {
|
||||
testarAcoesAtualizarStatus("Falha na execução. Consulte os detalhes técnicos.", "err");
|
||||
@@ -792,19 +792,19 @@ function copiarTextoParaAreaDeTransferencia(texto, mensagem) {
|
||||
else showToast("err", "Não foi possível copiar o JSON. Selecione e copie manualmente.");
|
||||
}
|
||||
|
||||
// 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", mensagem);
|
||||
}).catch(copiarComSelecaoDoPainel);
|
||||
return;
|
||||
}
|
||||
} catch (_) {}
|
||||
copiarComSelecaoDoPainel();
|
||||
}, mensagem);
|
||||
// A API nativa preserva o texto Unicode no clipboard do CEP/Chromium.
|
||||
// Use pbcopy apenas quando a API não existir ou falhar.
|
||||
try {
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(texto).then(function () {
|
||||
showToast("ok", mensagem);
|
||||
}).catch(function () {
|
||||
copiarComPbcopy(texto, copiarComSelecaoDoPainel, mensagem);
|
||||
});
|
||||
return;
|
||||
}
|
||||
} catch (_) {}
|
||||
copiarComPbcopy(texto, copiarComSelecaoDoPainel, mensagem);
|
||||
}
|
||||
|
||||
function copiarComPbcopy(texto, fallback, mensagem) {
|
||||
@@ -946,6 +946,8 @@ var SCANNER_PYTHON_BIN = "/Volumes/Merongo/SISTEMAS/venvs/whisperx-transcricao/b
|
||||
var ANALISE_STATUS_SCRIPT = "/Volumes/Merongo/SISTEMAS/GENIAL SISTEMAS/Jhonny/code/engine/consultar_status_da_analise.py";
|
||||
var ANALISE_PREVIEW_SCRIPT = "/Volumes/Merongo/SISTEMAS/GENIAL SISTEMAS/Jhonny/code/engine/consultar_preview_de_falantes.py";
|
||||
var REGISTRAR_EDICAO_SCRIPT = "/Volumes/Merongo/SISTEMAS/GENIAL SISTEMAS/Jhonny/code/engine/registrar_edicao.py";
|
||||
var CARREGAR_PLANO_SCRIPT = "/Volumes/Merongo/SISTEMAS/GENIAL SISTEMAS/Jhonny/code/engine/carregar_plano_de_edicao.py";
|
||||
var planoEditorialIdCarregado = null;
|
||||
var retakesCasoAtual = null;
|
||||
var retakesGrupos = [];
|
||||
var retakesProcesso = null;
|
||||
@@ -2699,12 +2701,15 @@ function silenceSelectSequence() {
|
||||
setStep(1, "done", "Timeline escolhida");
|
||||
lockStepsFrom(2);
|
||||
setStep(2, "ready", "Sua vez");
|
||||
setStep(3, "ready", "Carregar plano salvo");
|
||||
document.getElementById("btnLoadSavedEditorialPlan").disabled = false;
|
||||
document.getElementById("btnTranscribe").disabled = false;
|
||||
document.getElementById("silenceJsonCard").hidden = true;
|
||||
document.getElementById("jsonFileActions").hidden = true;
|
||||
document.getElementById("silencePlanInfo").textContent = "";
|
||||
silenceLog("Timeline escolhida: " + result.sequenceName + " — " + result.mediaPath);
|
||||
renderCachedTranscripts();
|
||||
carregarPlanoEditorialSalvo();
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -2716,9 +2721,10 @@ function silenceDetectClip() {
|
||||
// Volta os passos seguintes ao estado travado quando o vídeo (ou a transcrição)
|
||||
// muda — evita aplicar um plano gerado para outro material.
|
||||
function lockStepsFrom(first) {
|
||||
if (first <= 3) planoEditorialIdCarregado = null;
|
||||
var buttons = {
|
||||
2: ["btnBuildPlan"],
|
||||
3: ["btnApplyEditorialActions"],
|
||||
3: ["btnApplyEditorialActions", "btnLoadSavedEditorialPlan"],
|
||||
};
|
||||
for (var n = first; n <= 5; n++) {
|
||||
setStep(n, "locked", "Bloqueado");
|
||||
@@ -2815,6 +2821,7 @@ function runStreamed(bin, args, onLine, onDone, options) {
|
||||
var child = childProcess.spawn(bin, args, options || {});
|
||||
var buffer = "";
|
||||
var ultimoErro = "";
|
||||
var ultimaSaida = "";
|
||||
// Linhas "@@PROGRESS {json}" são o canal de progresso dos scripts Python/Node:
|
||||
// vão para a barra no topo em vez de virarem ruído no log.
|
||||
function handleLine(line) {
|
||||
@@ -2835,12 +2842,12 @@ function runStreamed(bin, args, onLine, onDone, options) {
|
||||
if (parts[i].trim()) handleLine(parts[i]);
|
||||
}
|
||||
}
|
||||
child.stdout.on("data", function (d) { flushLines(d.toString()); });
|
||||
child.stdout.on("data", function (d) { ultimaSaida += d.toString(); flushLines(d.toString()); });
|
||||
child.stderr.on("data", function (d) { ultimoErro = d.toString().trim() || ultimoErro; flushLines(d.toString()); });
|
||||
child.on("error", function (err) { onDone(1, "spawn error: " + err.message); });
|
||||
child.on("close", function (code) {
|
||||
if (buffer.trim()) handleLine(buffer);
|
||||
onDone(code, ultimoErro || null);
|
||||
onDone(code, ultimoErro || null, ultimaSaida);
|
||||
});
|
||||
return child;
|
||||
}
|
||||
@@ -3288,7 +3295,7 @@ function silenceConcluirEdicao() {
|
||||
SCANNER_PYTHON_BIN,
|
||||
[REGISTRAR_EDICAO_SCRIPT, caminhoDoPedido, "--banco", ANALISES_DB_PATH],
|
||||
function (linha) { silenceLog(linha); },
|
||||
function (codigo, ultimoErro) {
|
||||
function (codigo, ultimoErro, ultimaSaida) {
|
||||
setBusy("btnBuildPlan", false);
|
||||
if (codigo !== 0) {
|
||||
setStep(2, "error", "Falhou");
|
||||
@@ -3299,6 +3306,7 @@ function silenceConcluirEdicao() {
|
||||
}
|
||||
setStep(2, "done", "Edição concluída");
|
||||
setStep(3, "locked", "Aguardando plano da IA");
|
||||
document.getElementById("btnLoadSavedEditorialPlan").disabled = false;
|
||||
taskEnd(true, "Edição concluída e salva no banco.");
|
||||
silenceLog("Edição concluída e salva no banco de análises.", "ok");
|
||||
showToast("ok", "Edição concluída e salva no banco.");
|
||||
@@ -3306,11 +3314,49 @@ function silenceConcluirEdicao() {
|
||||
);
|
||||
}
|
||||
|
||||
// Busca o plano já registrado no SQLite; não cria JSON novo nem duplica plano.
|
||||
function carregarPlanoEditorialSalvo() {
|
||||
if (!silenceState.sequenceId) {
|
||||
showToast("err", "Detecte a sequência antes de carregar o plano.");
|
||||
return;
|
||||
}
|
||||
setBusy("btnLoadSavedEditorialPlan", true);
|
||||
silenceLog("Lendo o plano editorial diretamente do banco…");
|
||||
runStreamed(
|
||||
SCANNER_PYTHON_BIN,
|
||||
[CARREGAR_PLANO_SCRIPT, "--banco", ANALISES_DB_PATH, "--video-id", String(silenceState.sequenceId)],
|
||||
function (linha) { silenceLog(linha); },
|
||||
function (codigo, ultimoErro) {
|
||||
setBusy("btnLoadSavedEditorialPlan", false);
|
||||
if (codigo !== 0) {
|
||||
silenceLog("Não foi possível carregar o plano salvo.", "err");
|
||||
showToast("err", ultimoErro || "Nenhum plano salvo encontrado para este vídeo.");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var resposta = JSON.parse(ultimaSaida || "{}");
|
||||
if (!resposta.ok || !resposta.plano) throw new Error("Resposta inválida do banco.");
|
||||
planoEditorialIdCarregado = resposta.plano.plano_id;
|
||||
document.getElementById("editorialPlanJson").value = JSON.stringify(resposta.plano, null, 2);
|
||||
document.getElementById("editorialSavedPlanStatus").textContent = "Plano " + planoEditorialIdCarregado + " carregado do banco.";
|
||||
document.getElementById("editorialPlanJsonStatus").textContent = "Plano salvo no SQLite; pronto para aprovação e aplicação.";
|
||||
document.getElementById("btnApplyEditorialActions").disabled = false;
|
||||
setStep(3, "ready", "Plano carregado");
|
||||
silenceLog("Plano " + planoEditorialIdCarregado + " carregado diretamente do SQLite.", "ok");
|
||||
showToast("ok", "Plano salvo carregado.");
|
||||
} catch (erro) {
|
||||
showToast("err", erro.message);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Não gera decisões de corte: só empacota transcrição + tempos + locutores +
|
||||
// métricas de voz + configurações de edição num JSON para um agente de IA
|
||||
// externo analisar. Quem decide os cortes é a IA, não este painel.
|
||||
function silenceGenerateJson() {
|
||||
if (!silenceState.transcriptPath) return;
|
||||
planoEditorialIdCarregado = null;
|
||||
if (!silenceState.tipoVideo) {
|
||||
showToast("err", "Selecione o tipo de vídeo antes de gerar o JSON.");
|
||||
setStep(1, "ready", "Selecione um tipo");
|
||||
@@ -3457,7 +3503,9 @@ function applyEditorialActions() {
|
||||
});
|
||||
processoAplicacao = runStreamed(
|
||||
SCANNER_PYTHON_BIN,
|
||||
[TESTAR_ACOES_ENGINE_SCRIPT, caminhoDoPlano],
|
||||
[TESTAR_ACOES_ENGINE_SCRIPT, caminhoDoPlano].concat(
|
||||
planoEditorialIdCarregado === null ? [] : ["--plano-id", String(planoEditorialIdCarregado), "--banco", ANALISES_DB_PATH]
|
||||
),
|
||||
function (line) { silenceLog(line); },
|
||||
function (code, lastErr) {
|
||||
processoAplicacao = null;
|
||||
|
||||
Reference in New Issue
Block a user