Compare commits

...
2 Commits
Author SHA1 Message Date
João Henrique f02f4f3e60 feat: adicionada opção para calcular métricas de voz automaticamen
- adicionada opção para calcular métricas de voz automaticamente após a transcrição
- reorganizada a etapa opcional para revisar falantes e métricas processados automaticamente

Resumo:
- 2 arquivos alterados
- 0 novos
- 2 modificados
- 0 removidos

 2 files changed, 24 insertions(+), 8 deletions(-)

Arquivos:
  - code/cep-plugin/index.html
  - code/cep-plugin/main.js
2026-09-09 10:28:07 -04:00
João Henrique f3e8650a1a feat: exibida a quantidade de falantes detectados e o campo para n
- exibida a quantidade de falantes detectados e o campo para nomeá-los na guia Editar vídeo
- reposicionado o formulário de nomes dos falantes para ficar abaixo da opção de diarização

Resumo:
- 2 arquivos alterados
- 0 novos
- 2 modificados
- 0 removidos

 2 files changed, 17 insertions(+), 12 deletions(-)

Arquivos:
  - code/cep-plugin/index.html
  - code/cep-plugin/main.js
2026-09-09 10:15:03 -04:00
2 changed files with 41 additions and 20 deletions
+20 -14
View File
@@ -138,6 +138,20 @@
<span>Detectar quem fala logo após transcrever</span> <span>Detectar quem fala logo após transcrever</span>
</label> </label>
<label class="checkbox-field">
<input type="checkbox" id="silenceVoiceFeaturesAuto">
<span>Calcular métricas de voz logo após transcrever</span>
</label>
<div class="speaker-config" id="silenceSpeakerConfig" hidden>
<div class="speaker-config-head">
<strong>Falantes detectados</strong>
<span id="silenceSpeakerCount"></span>
</div>
<p class="field-help">Dê um nome a cada falante e desative quem não deve aparecer no vídeo. A decisão será enviada no JSON para a IA.</p>
<div id="silenceSpeakerList"></div>
</div>
<div class="mini-action" id="silenceRemovalRow"> <div class="mini-action" id="silenceRemovalRow">
<div> <div>
<label class="checkbox-field" style="border:none;padding:0;background:none"> <label class="checkbox-field" style="border:none;padding:0;background:none">
@@ -161,33 +175,25 @@
<div class="step-head"> <div class="step-head">
<span class="step-number" aria-hidden="true">3</span> <span class="step-number" aria-hidden="true">3</span>
<div class="step-title"> <div class="step-title">
<h2>Enriquecer a transcrição <span class="badge-optional">opcional</span></h2> <h2>Revisar falantes e áudio <span class="badge-optional">opcional</span></h2>
<p>Dados extras que deixam o corte por IA mais preciso.</p> <p>Confira os resultados processados e ajuste os dados antes de gerar o JSON.</p>
</div> </div>
<span class="step-chip" id="step3Chip">Bloqueado</span> <span class="step-chip" id="step3Chip">Bloqueado</span>
</div> </div>
<div class="step-body"> <div class="step-body">
<div class="mini-action"> <div class="mini-action">
<div> <div>
<strong>Quem fala</strong> <strong>Detecção de falantes</strong>
<span id="silenceDiarizeInfo">Separa os locutores por trecho. Precisa do token Hugging Face.</span> <span id="silenceDiarizeInfo">Separa os falantes por trecho. Precisa do token Hugging Face.</span>
</div> </div>
<button id="btnDiarize" class="button" onclick="silenceDiarizeSpeakers()" type="button" disabled>Detectar</button> <button id="btnDiarize" class="button" onclick="silenceDiarizeSpeakers()" type="button" disabled>Refazer</button>
</div>
<div class="speaker-config" id="silenceSpeakerConfig" hidden>
<div class="speaker-config-head">
<strong>Falantes identificados</strong>
<span id="silenceSpeakerCount"></span>
</div>
<p class="field-help">Dê um nome a cada falante e desative quem não deve aparecer no vídeo. A decisão será enviada no JSON para a IA.</p>
<div id="silenceSpeakerList"></div>
</div> </div>
<div class="mini-action"> <div class="mini-action">
<div> <div>
<strong>Métricas de voz</strong> <strong>Métricas de voz</strong>
<span id="silenceVoiceFeaturesInfo">Pitch, energia, velocidade de fala e pausas de cada trecho.</span> <span id="silenceVoiceFeaturesInfo">Pitch, energia, velocidade de fala e pausas de cada trecho.</span>
</div> </div>
<button id="btnVoiceFeatures" class="button" onclick="silenceComputeVoiceFeatures()" type="button" disabled>Calcular</button> <button id="btnVoiceFeatures" class="button" onclick="silenceComputeVoiceFeatures()" type="button" disabled>Refazer</button>
</div> </div>
</div> </div>
</section> </section>
+21 -6
View File
@@ -1786,15 +1786,25 @@ function silenceTranscribe() {
} }
taskEnd(true, "Transcrição pronta: " + resumo); taskEnd(true, "Transcrição pronta: " + resumo);
unlockAfterTranscript(); unlockAfterTranscript();
if (document.getElementById("silenceDiarize").checked) { silenceRunAutomaticEnrichments();
silenceDiarizeSpeakers();
}
}, },
hfSpawnOptions() hfSpawnOptions()
); );
} }
function silenceDiarizeSpeakers() { function silenceRunAutomaticEnrichments() {
var shouldDiarize = document.getElementById("silenceDiarize").checked && !!loadHfToken();
var shouldMeasureVoice = document.getElementById("silenceVoiceFeaturesAuto").checked;
if (shouldDiarize) {
silenceDiarizeSpeakers(function () {
if (shouldMeasureVoice) silenceComputeVoiceFeatures();
});
} else if (shouldMeasureVoice) {
silenceComputeVoiceFeatures();
}
}
function silenceDiarizeSpeakers(onFinished) {
if (!silenceState.transcriptPath) return; if (!silenceState.transcriptPath) return;
if (!silenceState.mediaPath) { if (!silenceState.mediaPath) {
silenceLog("Detecte o clipe/sequência primeiro (passo 1) para saber qual vídeo diarizar.", "err"); silenceLog("Detecte o clipe/sequência primeiro (passo 1) para saber qual vídeo diarizar.", "err");
@@ -1835,12 +1845,15 @@ function silenceDiarizeSpeakers() {
var parsed = null; var parsed = null;
try { parsed = JSON.parse(fs.readFileSync(silenceState.transcriptPath, "utf-8")).diarization; } catch (e) {} try { parsed = JSON.parse(fs.readFileSync(silenceState.transcriptPath, "utf-8")).diarization; } catch (e) {}
if (parsed && Array.isArray(parsed.speakers)) renderSpeakerConfiguration(parsed.speakers); if (parsed && Array.isArray(parsed.speakers)) renderSpeakerConfiguration(parsed.speakers);
var msg = parsed ? parsed.num_speakers + " locutor(es): " + parsed.speakers.join(", ") : "Locutores detectados."; var msg = parsed
? "Foram detectados " + parsed.num_speakers + (parsed.num_speakers === 1 ? " falante" : " falantes") + ": " + parsed.speakers.join(", ") + "."
: "Falantes detectados.";
silenceLog(msg, "ok"); silenceLog(msg, "ok");
info.textContent = msg; info.textContent = msg;
setStep(3, "done", "Enriquecido"); setStep(3, "done", "Enriquecido");
taskEnd(true, msg); taskEnd(true, msg);
syncTranscriptToCache(); syncTranscriptToCache();
if (typeof onFinished === "function") onFinished();
}, },
hfSpawnOptions() hfSpawnOptions()
); );
@@ -1865,7 +1878,8 @@ function renderSpeakerConfiguration(speakerIds) {
active: !old || old.active !== false, active: !old || old.active !== false,
}; };
}); });
count.textContent = silenceState.speakers.length + (silenceState.speakers.length === 1 ? " identificado" : " identificados"); count.textContent = silenceState.speakers.length +
(silenceState.speakers.length === 1 ? " falante detectado" : " falantes detectados");
list.innerHTML = ""; list.innerHTML = "";
silenceState.speakers.forEach(function (speaker, index) { silenceState.speakers.forEach(function (speaker, index) {
var row = document.createElement("div"); var row = document.createElement("div");
@@ -1877,6 +1891,7 @@ function renderSpeakerConfiguration(speakerIds) {
input.type = "text"; input.type = "text";
input.className = "select-field speaker-name-input"; input.className = "select-field speaker-name-input";
input.value = speaker.name; input.value = speaker.name;
input.placeholder = "Nome do falante";
input.maxLength = 120; input.maxLength = 120;
input.setAttribute("aria-label", "Nome de " + speaker.id); input.setAttribute("aria-label", "Nome de " + speaker.id);
input.addEventListener("input", function () { silenceState.speakers[index].name = input.value.trim() || speaker.id; }); input.addEventListener("input", function () { silenceState.speakers[index].name = input.value.trim() || speaker.id; });