feat: aprimorado o preview de falantes na tela Refinar para reprod
- aprimorado o preview de falantes na tela Refinar para reproduzir os trechos do vídeo - corrigida a leitura dos timestamps do preview de falantes usando os clipes do banco - alterado o preview de falantes para reproduzir diretamente na timeline ativa do Premiere Resumo: - 6 arquivos alterados - 0 novos - 4 modificados - 2 removidos 6 files changed, 68 insertions(+), 53 deletions(-) Arquivos: - .jhonny/analises.db - .jhonny/analises.db-shm - .jhonny/analises.db-wal - code/cep-plugin/index.html - code/cep-plugin/main.js - code/cep-plugin/styles.css
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -375,7 +375,7 @@
|
|||||||
<div class="callout callout-info" id="refinarSpeakerEmpty"><strong>Nenhum falante identificado</strong><p>A análise carregada não possui diarização. Execute a diarização no Scanner para liberar esta etapa.</p></div>
|
<div class="callout callout-info" id="refinarSpeakerEmpty"><strong>Nenhum falante identificado</strong><p>A análise carregada não possui diarização. Execute a diarização no Scanner para liberar esta etapa.</p></div>
|
||||||
<div class="speaker-config" id="refinarSpeakerConfig" hidden>
|
<div class="speaker-config" id="refinarSpeakerConfig" hidden>
|
||||||
<div class="speaker-video-preview" id="refinarSpeakerPreview" hidden>
|
<div class="speaker-video-preview" id="refinarSpeakerPreview" hidden>
|
||||||
<video id="refinarSpeakerVideo" controls preload="metadata" playsinline></video>
|
<strong>Preview na timeline do Premiere</strong>
|
||||||
<p class="field-help" id="refinarSpeakerPreviewInfo">Reproduzindo os trechos do falante selecionado.</p>
|
<p class="field-help" id="refinarSpeakerPreviewInfo">Reproduzindo os trechos do falante selecionado.</p>
|
||||||
</div>
|
</div>
|
||||||
<div id="refinarSpeakerList"></div>
|
<div id="refinarSpeakerList"></div>
|
||||||
|
|||||||
+66
-51
@@ -2327,8 +2327,8 @@ function silenceLoadScannerAnalysis() {
|
|||||||
turnos = intervalosDoBanco.map(function (intervalo) {
|
turnos = intervalosDoBanco.map(function (intervalo) {
|
||||||
return {
|
return {
|
||||||
speaker: intervalo.falante,
|
speaker: intervalo.falante,
|
||||||
start: intervalo.inicio,
|
start: intervalo.inicio_na_timeline,
|
||||||
end: intervalo.fim,
|
end: intervalo.fim_na_timeline,
|
||||||
timelineStart: intervalo.inicio_na_timeline,
|
timelineStart: intervalo.inicio_na_timeline,
|
||||||
timelineEnd: intervalo.fim_na_timeline,
|
timelineEnd: intervalo.fim_na_timeline,
|
||||||
mediaPath: intervalo.arquivo,
|
mediaPath: intervalo.arquivo,
|
||||||
@@ -2405,8 +2405,9 @@ function atualizarTelaRefinar(falantes, audioDoBanco) {
|
|||||||
|
|
||||||
var speakerAudioPlayer = null;
|
var speakerAudioPlayer = null;
|
||||||
var speakerAudioQueue = null;
|
var speakerAudioQueue = null;
|
||||||
var speakerVideoPlayer = null;
|
var speakerTimelineQueue = null;
|
||||||
var speakerVideoQueue = null;
|
var speakerTimelinePoll = null;
|
||||||
|
var speakerTimelineToken = 0;
|
||||||
|
|
||||||
// ---- Central transcript cache ----
|
// ---- Central transcript cache ----
|
||||||
// Evita rodar o Whisper de novo para um vídeo já transcrito antes: indexa
|
// Evita rodar o Whisper de novo para um vídeo já transcrito antes: indexa
|
||||||
@@ -3017,7 +3018,7 @@ function renderSpeakerConfiguration(speakerIds, speakerTurns, alvo) {
|
|||||||
play.title = (alvo.video ? "Ver" : "Ouvir") + " trechos de " + speaker.id;
|
play.title = (alvo.video ? "Ver" : "Ouvir") + " trechos de " + speaker.id;
|
||||||
play.setAttribute("aria-label", (alvo.video ? "Ver" : "Ouvir") + " trechos de " + speaker.id);
|
play.setAttribute("aria-label", (alvo.video ? "Ver" : "Ouvir") + " trechos de " + speaker.id);
|
||||||
play.addEventListener("click", function () {
|
play.addEventListener("click", function () {
|
||||||
if (alvo.video) toggleSpeakerVideo(speaker.id, play);
|
if (alvo.video) toggleSpeakerTimeline(speaker.id, play);
|
||||||
else toggleSpeakerAudio(speaker.id, play);
|
else toggleSpeakerAudio(speaker.id, play);
|
||||||
});
|
});
|
||||||
var input = document.createElement("input");
|
var input = document.createElement("input");
|
||||||
@@ -3071,17 +3072,13 @@ function stopSpeakerAudio() {
|
|||||||
}
|
}
|
||||||
speakerAudioPlayer = null;
|
speakerAudioPlayer = null;
|
||||||
speakerAudioQueue = null;
|
speakerAudioQueue = null;
|
||||||
if (speakerVideoPlayer) {
|
speakerTimelineToken += 1;
|
||||||
speakerVideoPlayer.pause();
|
if (speakerTimelinePoll) {
|
||||||
speakerVideoPlayer.onloadedmetadata = null;
|
clearInterval(speakerTimelinePoll);
|
||||||
speakerVideoPlayer.ontimeupdate = null;
|
speakerTimelinePoll = null;
|
||||||
speakerVideoPlayer.onended = null;
|
|
||||||
speakerVideoPlayer.onerror = null;
|
|
||||||
speakerVideoPlayer.removeAttribute("src");
|
|
||||||
speakerVideoPlayer.load();
|
|
||||||
}
|
}
|
||||||
speakerVideoPlayer = null;
|
speakerTimelineQueue = null;
|
||||||
speakerVideoQueue = null;
|
cs.evalScript("(function(){try{if(typeof qe!==\"undefined\"&&qe.stopPlayback)qe.stopPlayback();return \"ok\";}catch(e){return \"erro:\"+String(e);}}())", function () {});
|
||||||
var videoPreview = document.getElementById("refinarSpeakerPreview");
|
var videoPreview = document.getElementById("refinarSpeakerPreview");
|
||||||
if (videoPreview) videoPreview.hidden = true;
|
if (videoPreview) videoPreview.hidden = true;
|
||||||
document.querySelectorAll(".speaker-play-button").forEach(function (button) {
|
document.querySelectorAll(".speaker-play-button").forEach(function (button) {
|
||||||
@@ -3090,8 +3087,8 @@ function stopSpeakerAudio() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleSpeakerVideo(speakerId, button) {
|
function toggleSpeakerTimeline(speakerId, button) {
|
||||||
if (speakerVideoPlayer && speakerVideoQueue && speakerVideoQueue.speakerId === speakerId) {
|
if (speakerTimelineQueue && speakerTimelineQueue.speakerId === speakerId) {
|
||||||
stopSpeakerAudio();
|
stopSpeakerAudio();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -3103,53 +3100,71 @@ function toggleSpeakerVideo(speakerId, button) {
|
|||||||
showToast("err", "Não há intervalos de vídeo disponíveis para " + speakerId + ".");
|
showToast("err", "Não há intervalos de vídeo disponíveis para " + speakerId + ".");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!silenceState.mediaPath || !fs.existsSync(silenceState.mediaPath)) {
|
|
||||||
showToast("err", "Arquivo de vídeo da análise não encontrado.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var video = document.getElementById("refinarSpeakerVideo");
|
|
||||||
var preview = document.getElementById("refinarSpeakerPreview");
|
var preview = document.getElementById("refinarSpeakerPreview");
|
||||||
var info = document.getElementById("refinarSpeakerPreviewInfo");
|
var info = document.getElementById("refinarSpeakerPreviewInfo");
|
||||||
if (!video || !preview) {
|
if (!preview) {
|
||||||
showToast("err", "Preview de vídeo indisponível nesta versão do painel.");
|
showToast("err", "Preview de vídeo indisponível nesta versão do painel.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
speakerVideoQueue = { speakerId: speakerId, ranges: ranges, index: 0, button: button };
|
speakerTimelineQueue = { speakerId: speakerId, ranges: ranges, index: 0, button: button };
|
||||||
speakerVideoPlayer = video;
|
|
||||||
preview.hidden = false;
|
preview.hidden = false;
|
||||||
if (info) info.textContent = "Reproduzindo os trechos de " + speakerId + ".";
|
if (info) info.textContent = "Reproduzindo os trechos de " + speakerId + ".";
|
||||||
video.onloadedmetadata = playCurrentSpeakerVideoRange;
|
button.textContent = "■ timeline";
|
||||||
video.ontimeupdate = advanceSpeakerVideoRange;
|
|
||||||
video.onended = advanceSpeakerVideoRange;
|
|
||||||
video.onerror = function () {
|
|
||||||
stopSpeakerAudio();
|
|
||||||
showToast("err", "Não foi possível reproduzir o vídeo deste falante.");
|
|
||||||
};
|
|
||||||
button.textContent = "■ vídeo";
|
|
||||||
button.classList.add("is-playing");
|
button.classList.add("is-playing");
|
||||||
video.src = speakerMediaFileUrl(silenceState.mediaPath);
|
iniciarPreviewNaTimeline(speakerTimelineToken);
|
||||||
video.load();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function playCurrentSpeakerVideoRange() {
|
function iniciarPreviewNaTimeline(token) {
|
||||||
if (!speakerVideoPlayer || !speakerVideoQueue) return;
|
if (!speakerTimelineQueue || token !== speakerTimelineToken) return;
|
||||||
var range = speakerVideoQueue.ranges[speakerVideoQueue.index];
|
var range = speakerTimelineQueue.ranges[speakerTimelineQueue.index];
|
||||||
if (!range) { stopSpeakerAudio(); return; }
|
if (!range) { stopSpeakerAudio(); return; }
|
||||||
speakerVideoPlayer.currentTime = Math.max(0, Number(range.start) - 0.15);
|
var inicio = Math.max(0, Number(range.start) - 0.15);
|
||||||
var playPromise = speakerVideoPlayer.play();
|
var inicioJson = JSON.stringify(inicio);
|
||||||
if (playPromise && playPromise.catch) playPromise.catch(function () { stopSpeakerAudio(); });
|
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) 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 iniciar o preview na timeline.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
monitorarPreviewNaTimeline(token);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function advanceSpeakerVideoRange() {
|
function monitorarPreviewNaTimeline(token) {
|
||||||
if (!speakerVideoPlayer || !speakerVideoQueue) return;
|
if (speakerTimelinePoll) clearInterval(speakerTimelinePoll);
|
||||||
var range = speakerVideoQueue.ranges[speakerVideoQueue.index];
|
speakerTimelinePoll = setInterval(function () {
|
||||||
if (!range || speakerVideoPlayer.currentTime < Number(range.end)) return;
|
if (!speakerTimelineQueue || token !== speakerTimelineToken) return;
|
||||||
speakerVideoQueue.index += 1;
|
var range = speakerTimelineQueue.ranges[speakerTimelineQueue.index];
|
||||||
if (speakerVideoQueue.index >= speakerVideoQueue.ranges.length) {
|
cs.evalScript(
|
||||||
stopSpeakerAudio();
|
"(function(){try{var seq=app.project.activeSequence;return JSON.stringify({ok:!!seq,position:seq?Number(seq.getPlayerPosition().ticks)/254016000000:0});}catch(e){return JSON.stringify({ok:false,error:String(e)});}}())",
|
||||||
return;
|
function (raw) {
|
||||||
}
|
if (!speakerTimelineQueue || token !== speakerTimelineToken) return;
|
||||||
playCurrentSpeakerVideoRange();
|
var resultado;
|
||||||
|
try { resultado = JSON.parse(raw); } catch (_) { return; }
|
||||||
|
if (!resultado.ok || Number(resultado.position) >= Number(range.end)) {
|
||||||
|
speakerTimelineQueue.index += 1;
|
||||||
|
if (speakerTimelineQueue.index >= speakerTimelineQueue.ranges.length) {
|
||||||
|
stopSpeakerAudio();
|
||||||
|
} else {
|
||||||
|
iniciarPreviewNaTimeline(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}, 120);
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleSpeakerAudio(speakerId, button) {
|
function toggleSpeakerAudio(speakerId, button) {
|
||||||
|
|||||||
@@ -492,7 +492,7 @@ textarea { padding: 8px 10px; font: 10px var(--font-mono); color: var(--text-sec
|
|||||||
.speaker-name-input { width: 100%; }
|
.speaker-name-input { width: 100%; }
|
||||||
.speaker-active-field { margin: 0; padding: 6px 8px; white-space: nowrap; }
|
.speaker-active-field { margin: 0; padding: 6px 8px; white-space: nowrap; }
|
||||||
.speaker-video-preview { margin: 0 0 10px; padding-bottom: 10px; border-bottom: 1px solid var(--border); }
|
.speaker-video-preview { margin: 0 0 10px; padding-bottom: 10px; border-bottom: 1px solid var(--border); }
|
||||||
.speaker-video-preview video { display: block; width: 100%; max-height: 260px; border-radius: 5px; background: #000; }
|
.speaker-video-preview strong { display: block; color: var(--accent); font-size: 10px; }
|
||||||
.speaker-video-preview .field-help { margin: 6px 0 0; }
|
.speaker-video-preview .field-help { margin: 6px 0 0; }
|
||||||
.refinar-actions { display: flex; gap: 8px; flex-wrap: wrap; }
|
.refinar-actions { display: flex; gap: 8px; flex-wrap: wrap; }
|
||||||
.refinar-actions .button { flex: 1 1 180px; }
|
.refinar-actions .button { flex: 1 1 180px; }
|
||||||
|
|||||||
Reference in New Issue
Block a user