feat: corrigida a reprodução do trecho de áudio do falante na tela
- corrigida a reprodução do trecho de áudio do falante na tela Refinar usando os tempos da origem do WAV. - alterado o passo de geração de JSON para concluir a edição e salvar a configuração editorial no banco. Resumo: - 9 arquivos alterados - 3 novos - 6 modificados - 0 removidos 6 files changed, 136 insertions(+), 109 deletions(-) Arquivos: - .jhonny/analises.db - code/cep-plugin/index.html - code/cep-plugin/main.js - code/engine/persistencia/__init__.py - code/engine/persistencia/esquema.py - code/engine/testes/test_persistencia_sqlite.py - code/engine/persistencia/repositorio_de_edicoes.py - code/engine/registrar_edicao.py - code/tests/cep-refinar-preview.test.ts
This commit is contained in:
+89
-105
@@ -945,6 +945,7 @@ var RETAKES_ENGINE_SCRIPT = "/Volumes/Merongo/SISTEMAS/GENIAL SISTEMAS/Jhonny/co
|
||||
var SCANNER_PYTHON_BIN = "/Volumes/Merongo/SISTEMAS/venvs/whisperx-transcricao/bin/python";
|
||||
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 retakesCasoAtual = null;
|
||||
var retakesGrupos = [];
|
||||
var retakesProcesso = null;
|
||||
@@ -2346,9 +2347,24 @@ function silenceLoadScannerAnalysis() {
|
||||
atualizarCheckDaAnalise("scannerCheckTranscription", temTranscricao, temTranscricao ? "Carregado" : "Não encontrada");
|
||||
atualizarCheckDaAnalise("scannerCheckDiarization", temDiarizacao, temDiarizacao ? "Carregado" : "Não realizada");
|
||||
atualizarCheckDaAnalise("scannerCheckVoiceFeatures", temMetricasDeVoz, temMetricasDeVoz ? "Carregado" : "Não realizada");
|
||||
var turnos = transcricao.segments.filter(function (segmento) { return segmento.falante; }).map(function (segmento) {
|
||||
return { speaker: segmento.falante, start: segmento.start, end: segmento.end };
|
||||
});
|
||||
var intervalosDePreview = silenceConsultarPreviewDeFalantes(silenceState.sequenceId);
|
||||
var turnos = intervalosDePreview && intervalosDePreview.length
|
||||
? intervalosDePreview.filter(function (intervalo) {
|
||||
return intervalo.falante && Number(intervalo.fim) > Number(intervalo.inicio);
|
||||
}).map(function (intervalo) {
|
||||
return {
|
||||
speaker: intervalo.falante,
|
||||
start: Number(intervalo.inicio),
|
||||
end: Number(intervalo.fim),
|
||||
};
|
||||
})
|
||||
: transcricao.segments.filter(function (segmento) { return segmento.falante; }).map(function (segmento) {
|
||||
return {
|
||||
speaker: segmento.falante,
|
||||
start: Number(segmento.start),
|
||||
end: Number(segmento.end),
|
||||
};
|
||||
});
|
||||
renderSpeakerConfiguration(falantes, turnos);
|
||||
atualizarTelaRefinar(falantes, audioDoBanco);
|
||||
status.textContent = "Carregada";
|
||||
@@ -2418,9 +2434,6 @@ function atualizarTelaRefinar(falantes, audioDoBanco) {
|
||||
|
||||
var speakerAudioPlayer = null;
|
||||
var speakerAudioQueue = null;
|
||||
var speakerTimelineQueue = null;
|
||||
var speakerTimelineTimer = null;
|
||||
var speakerTimelineToken = 0;
|
||||
|
||||
// ---- Central transcript cache ----
|
||||
// Evita rodar o Whisper de novo para um vídeo já transcrito antes: indexa
|
||||
@@ -3090,105 +3103,6 @@ function stopSpeakerAudio() {
|
||||
});
|
||||
}
|
||||
|
||||
function toggleSpeakerTimeline(speakerId, button) {
|
||||
if (speakerTimelineQueue && speakerTimelineQueue.speakerId === speakerId) {
|
||||
stopSpeakerAudio();
|
||||
return;
|
||||
}
|
||||
stopSpeakerAudio();
|
||||
var ranges = silenceState.speakerTurns.filter(function (turn) {
|
||||
return turn.speaker === speakerId && Number(turn.end) > Number(turn.start);
|
||||
}).sort(function (a, b) { return Number(a.start) - Number(b.start); });
|
||||
if (!ranges.length) {
|
||||
showToast("err", "Não há intervalos de vídeo disponíveis para " + speakerId + ".");
|
||||
return;
|
||||
}
|
||||
var preview = document.getElementById("refinarSpeakerPreview");
|
||||
var info = document.getElementById("refinarSpeakerPreviewInfo");
|
||||
if (!preview) {
|
||||
showToast("err", "Preview de vídeo indisponível nesta versão do painel.");
|
||||
return;
|
||||
}
|
||||
speakerTimelineQueue = {
|
||||
speakerId: speakerId,
|
||||
ranges: ranges,
|
||||
index: 0,
|
||||
button: button,
|
||||
inicioPendente: false,
|
||||
};
|
||||
preview.hidden = false;
|
||||
if (info) info.textContent = "Reproduzindo os trechos de " + speakerId + ".";
|
||||
button.textContent = "■ timeline";
|
||||
button.classList.add("is-playing");
|
||||
iniciarPreviewNaTimeline(speakerTimelineToken);
|
||||
}
|
||||
|
||||
function iniciarPreviewNaTimeline(token) {
|
||||
if (!speakerTimelineQueue || token !== speakerTimelineToken) return;
|
||||
var indiceDoTrecho = speakerTimelineQueue.index;
|
||||
if (speakerTimelineQueue.inicioPendente) return;
|
||||
var range = speakerTimelineQueue.ranges[indiceDoTrecho];
|
||||
if (!range) { stopSpeakerAudio(); return; }
|
||||
var inicio = Math.max(0, Number(range.start) - 0.15);
|
||||
var inicioJson = JSON.stringify(inicio);
|
||||
speakerTimelineQueue.inicioPendente = true;
|
||||
cs.evalScript(
|
||||
"(function(){try{" +
|
||||
"var seq=app.project.activeSequence;" +
|
||||
"if(!seq)return JSON.stringify({ok:false,error:'Nenhuma sequência ativa.'});" +
|
||||
"if(String(seq.sequenceID)!==" + JSON.stringify(String(silenceState.sequenceId)) + ")return JSON.stringify({ok:false,error:'A sequência da análise não está ativa.'});" +
|
||||
"seq.setPlayerPosition(String(Math.round(" + inicioJson + "*254016000000)));" +
|
||||
"app.enableQE();qe.startPlayback();" +
|
||||
"return JSON.stringify({ok:true});" +
|
||||
"}catch(e){return JSON.stringify({ok:false,error:String(e)});}}())",
|
||||
function (raw) {
|
||||
if (!speakerTimelineQueue || token !== speakerTimelineToken || speakerTimelineQueue.index !== indiceDoTrecho) return;
|
||||
speakerTimelineQueue.inicioPendente = false;
|
||||
var resultado;
|
||||
try { resultado = JSON.parse(raw); } catch (_) { resultado = { ok: false, error: "Resposta inválida do Premiere." }; }
|
||||
if (!resultado.ok) {
|
||||
stopSpeakerAudio();
|
||||
showToast("err", resultado.error || "Não foi possível iniciar o preview na timeline.");
|
||||
return;
|
||||
}
|
||||
agendarFimDoTrechoNaTimeline(token, indiceDoTrecho, inicio, Number(range.end));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function agendarFimDoTrechoNaTimeline(token, indiceDoTrecho, inicio, fim) {
|
||||
if (!speakerTimelineQueue || token !== speakerTimelineToken || speakerTimelineQueue.index !== indiceDoTrecho) return;
|
||||
var duracaoEmMilissegundos = Math.max(250, (fim - inicio) * 1000);
|
||||
speakerTimelineTimer = setTimeout(function () {
|
||||
speakerTimelineTimer = null;
|
||||
if (!speakerTimelineQueue || token !== speakerTimelineToken || speakerTimelineQueue.index !== indiceDoTrecho) return;
|
||||
pausarEAvancarPreviewNaTimeline(token);
|
||||
}, duracaoEmMilissegundos);
|
||||
}
|
||||
|
||||
function pausarEAvancarPreviewNaTimeline(token) {
|
||||
if (!speakerTimelineQueue || token !== speakerTimelineToken) return;
|
||||
cs.evalScript(
|
||||
"(function(){try{if(typeof qe!==\"undefined\"&&qe.stopPlayback)qe.stopPlayback();return JSON.stringify({ok:true});}catch(e){return JSON.stringify({ok:false,error:String(e)});}}())",
|
||||
function (raw) {
|
||||
if (!speakerTimelineQueue || token !== speakerTimelineToken) return;
|
||||
var resultado;
|
||||
try { resultado = JSON.parse(raw); } catch (_) { resultado = { ok: false, error: "Resposta inválida do Premiere." }; }
|
||||
if (!resultado.ok) {
|
||||
stopSpeakerAudio();
|
||||
showToast("err", resultado.error || "Não foi possível pausar o preview na timeline.");
|
||||
return;
|
||||
}
|
||||
speakerTimelineQueue.index += 1;
|
||||
if (speakerTimelineQueue.index >= speakerTimelineQueue.ranges.length) {
|
||||
stopSpeakerAudio();
|
||||
} else {
|
||||
iniciarPreviewNaTimeline(token);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function toggleSpeakerAudio(speakerId, button) {
|
||||
if (speakerAudioPlayer && speakerAudioQueue && speakerAudioQueue.speakerId === speakerId) {
|
||||
stopSpeakerAudio();
|
||||
@@ -3328,6 +3242,76 @@ function silenceComputeVoiceFeatures() {
|
||||
}
|
||||
*/
|
||||
|
||||
// Registra a decisão editorial sem duplicar a análise normalizada do Scanner.
|
||||
// O JSON continua disponível para o fluxo da IA, mas não é pré-requisito para
|
||||
// concluir a edição no painel.
|
||||
function silenceConcluirEdicao() {
|
||||
if (!silenceState.sequenceId || !silenceState.sequenceName) {
|
||||
showToast("err", "Carregue uma análise do Scanner antes de concluir.");
|
||||
return;
|
||||
}
|
||||
if (!silenceState.tipoVideo) {
|
||||
showToast("err", "Selecione o tipo de vídeo antes de concluir a edição.");
|
||||
setStep(1, "ready", "Selecione um tipo");
|
||||
return;
|
||||
}
|
||||
|
||||
var pedido = {
|
||||
video_id: String(silenceState.sequenceId),
|
||||
sequencia: silenceState.sequenceName,
|
||||
origem: silenceState.mediaPath,
|
||||
tipo_de_video: silenceState.tipoVideo.id,
|
||||
configuracao: {
|
||||
nome_do_tipo: silenceState.tipoVideo.nome,
|
||||
instrucoes_do_tipo: tipoVideoResumoParaJson(silenceState.tipoVideo),
|
||||
configuracoes_de_fala: speakerConfigurationForJson(),
|
||||
configuracoes_de_edicao: {
|
||||
remocao_de_silencio: {
|
||||
habilitada: document.getElementById("silenceRemovalEnabled").checked,
|
||||
duracao_minima_segundos: Number(document.getElementById("silenceRemovalMinDuration").value) || 0.8,
|
||||
},
|
||||
legendas: carregarConfiguracaoDeLegendas(),
|
||||
},
|
||||
transcricao: {
|
||||
caminho: silenceState.transcriptPath,
|
||||
origem: "Scanner",
|
||||
},
|
||||
},
|
||||
};
|
||||
var caminhoDoPedido = path.join(os.tmpdir(), "pedido-concluir-edicao-" + Date.now() + ".json");
|
||||
try {
|
||||
fs.writeFileSync(caminhoDoPedido, JSON.stringify(pedido, null, 2), "utf-8");
|
||||
} catch (erroEscrita) {
|
||||
showToast("err", "Não foi possível preparar a conclusão: " + erroEscrita.message);
|
||||
return;
|
||||
}
|
||||
|
||||
setBusy("btnBuildPlan", true);
|
||||
setStep(4, "running", "Salvando…");
|
||||
taskStart("Concluindo a edição", "Salvando a configuração no banco");
|
||||
silenceLog("Salvando o tipo de vídeo e as configurações editoriais no banco…");
|
||||
runStreamed(
|
||||
SCANNER_PYTHON_BIN,
|
||||
[REGISTRAR_EDICAO_SCRIPT, caminhoDoPedido, "--banco", ANALISES_DB_PATH],
|
||||
function (linha) { silenceLog(linha); },
|
||||
function (codigo, ultimoErro) {
|
||||
setBusy("btnBuildPlan", false);
|
||||
if (codigo !== 0) {
|
||||
setStep(4, "error", "Falhou");
|
||||
taskEnd(false, "Não foi possível concluir a edição.", ultimoErro);
|
||||
silenceLog("Falha ao salvar a configuração editorial.", "err");
|
||||
showToast("err", ultimoErro || "Não foi possível salvar a edição no banco.");
|
||||
return;
|
||||
}
|
||||
setStep(4, "done", "Edição concluída");
|
||||
setStep(5, "locked", "Aguardando plano da IA");
|
||||
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.");
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
Reference in New Issue
Block a user