Compare commits
2
Commits
69e931eb45
...
b6c1b79336
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6c1b79336 | ||
|
|
bea38cfeb3 |
@@ -79,18 +79,23 @@
|
|||||||
<span class="step-number" aria-hidden="true">1</span>
|
<span class="step-number" aria-hidden="true">1</span>
|
||||||
<div class="step-title">
|
<div class="step-title">
|
||||||
<h2>Escolher o vídeo</h2>
|
<h2>Escolher o vídeo</h2>
|
||||||
<p>Lê o primeiro clipe da sequência aberta no Premiere.</p>
|
<p>Lista as timelines do projeto e lê o primeiro clipe da escolhida.</p>
|
||||||
</div>
|
</div>
|
||||||
<span class="step-chip" id="step1Chip">Comece aqui</span>
|
<span class="step-chip" id="step1Chip">Comece aqui</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="step-body">
|
<div class="step-body">
|
||||||
<button id="btnDetectClip" class="button button-primary" onclick="silenceDetectClip()" type="button">Detectar sequência ativa</button>
|
<button id="btnDetectClip" class="button button-primary" onclick="silenceListSequences()" type="button">Carregar sequências</button>
|
||||||
|
<div class="sequence-picker" id="silenceSequencePicker" hidden>
|
||||||
|
<label class="field-label" for="silenceSequenceSelect">Escolha a timeline</label>
|
||||||
|
<select id="silenceSequenceSelect" class="select-field" onchange="silenceSelectSequence()"></select>
|
||||||
|
<p class="field-help" id="silenceSequenceListInfo"></p>
|
||||||
|
</div>
|
||||||
<dl class="kv" id="silenceClipCard" hidden>
|
<dl class="kv" id="silenceClipCard" hidden>
|
||||||
<div><dt>Sequência</dt><dd id="clipSequenceName">—</dd></div>
|
<div><dt>Sequência</dt><dd id="clipSequenceName">—</dd></div>
|
||||||
<div><dt>Arquivo</dt><dd id="clipFileName">—</dd></div>
|
<div><dt>Arquivo</dt><dd id="clipFileName">—</dd></div>
|
||||||
<div><dt>Caminho</dt><dd class="kv-path" id="clipMediaPath">—</dd></div>
|
<div><dt>Caminho</dt><dd class="kv-path" id="clipMediaPath">—</dd></div>
|
||||||
</dl>
|
</dl>
|
||||||
<p class="field-help" id="silenceClipInfo">Abra a sequência desejada no Premiere e clique em Detectar.</p>
|
<p class="field-help" id="silenceClipInfo">Abra o projeto no Premiere e carregue as timelines.</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -169,6 +174,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<button id="btnDiarize" class="button" onclick="silenceDiarizeSpeakers()" type="button" disabled>Detectar</button>
|
<button id="btnDiarize" class="button" onclick="silenceDiarizeSpeakers()" type="button" disabled>Detectar</button>
|
||||||
</div>
|
</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 class="mini-action">
|
<div class="mini-action">
|
||||||
<div>
|
<div>
|
||||||
<strong>Métricas de voz</strong>
|
<strong>Métricas de voz</strong>
|
||||||
|
|||||||
+198
-14
@@ -1323,6 +1323,7 @@ var LANGUAGE_CATALOG = [
|
|||||||
var DEFAULT_LANGUAGE = "pt";
|
var DEFAULT_LANGUAGE = "pt";
|
||||||
|
|
||||||
var silenceState = {
|
var silenceState = {
|
||||||
|
sequenceId: null,
|
||||||
sequenceName: null,
|
sequenceName: null,
|
||||||
mediaPath: null,
|
mediaPath: null,
|
||||||
fileBaseName: null,
|
fileBaseName: null,
|
||||||
@@ -1331,6 +1332,7 @@ var silenceState = {
|
|||||||
transcribeModel: null,
|
transcribeModel: null,
|
||||||
cachedTranscriptPath: null,
|
cachedTranscriptPath: null,
|
||||||
jsonOutputPath: null,
|
jsonOutputPath: null,
|
||||||
|
speakers: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
// ---- Central transcript cache ----
|
// ---- Central transcript cache ----
|
||||||
@@ -1441,21 +1443,108 @@ function slugify(name) {
|
|||||||
.replace(/(^-|-$)/g, "") || "video";
|
.replace(/(^-|-$)/g, "") || "video";
|
||||||
}
|
}
|
||||||
|
|
||||||
function silenceDetectClip() {
|
function formatSequenceDuration(seconds) {
|
||||||
|
var total = Math.max(0, Math.round(Number(seconds) || 0));
|
||||||
|
var minutes = Math.floor(total / 60);
|
||||||
|
var remainder = total % 60;
|
||||||
|
return minutes + ":" + String(remainder).padStart(2, "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
function silenceListSequences() {
|
||||||
setBusy("btnDetectClip", true);
|
setBusy("btnDetectClip", true);
|
||||||
setStep(1, "running", "Detectando…");
|
setStep(1, "running", "Carregando…");
|
||||||
document.getElementById("silenceClipInfo").textContent = "Lendo a sequência ativa no Premiere…";
|
document.getElementById("silenceClipInfo").textContent = "Lendo as timelines do projeto…";
|
||||||
cs.evalScript(
|
cs.evalScript(
|
||||||
"(function(){" +
|
"(function(){" +
|
||||||
"try{" +
|
"try{" +
|
||||||
"var seq=app.project.activeSequence;" +
|
"if(!app.project) return JSON.stringify({ok:false,error:'Nenhum projeto aberto.'});" +
|
||||||
"if(!seq) return JSON.stringify({ok:false,error:'Nenhuma sequência ativa.'});" +
|
"var sequences=[];" +
|
||||||
|
"for(var i=0;i<app.project.sequences.numSequences;i++){" +
|
||||||
|
"var seq=app.project.sequences[i];" +
|
||||||
|
"sequences.push({id:String(seq.sequenceID),name:String(seq.name),duration:Number(seq.end.seconds)||0," +
|
||||||
|
"videoTracks:seq.videoTracks.numTracks||0,audioTracks:seq.audioTracks.numTracks||0," +
|
||||||
|
"active:app.project.activeSequence&&seq.sequenceID===app.project.activeSequence.sequenceID});" +
|
||||||
|
"}" +
|
||||||
|
"return JSON.stringify({ok:true,sequences:sequences});" +
|
||||||
|
"}catch(e){return JSON.stringify({ok:false,error:String(e)});}" +
|
||||||
|
"}())",
|
||||||
|
function (raw) {
|
||||||
|
setBusy("btnDetectClip", false);
|
||||||
|
var result;
|
||||||
|
try {
|
||||||
|
result = JSON.parse(raw);
|
||||||
|
} catch (e) {
|
||||||
|
result = { ok: false, error: "Resposta inesperada do Premiere." };
|
||||||
|
}
|
||||||
|
var picker = document.getElementById("silenceSequencePicker");
|
||||||
|
var select = document.getElementById("silenceSequenceSelect");
|
||||||
|
var info = document.getElementById("silenceClipInfo");
|
||||||
|
|
||||||
|
if (!result.ok) {
|
||||||
|
picker.hidden = true;
|
||||||
|
setStep(1, "error", "Não encontrado");
|
||||||
|
info.textContent = result.error || "Não foi possível listar as timelines.";
|
||||||
|
showToast("err", info.textContent);
|
||||||
|
lockStepsFrom(2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!result.sequences || result.sequences.length === 0) {
|
||||||
|
picker.hidden = true;
|
||||||
|
setStep(1, "error", "Sem timelines");
|
||||||
|
info.textContent = "O projeto não possui timelines.";
|
||||||
|
showToast("err", info.textContent);
|
||||||
|
lockStepsFrom(2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
select.innerHTML = "";
|
||||||
|
result.sequences.forEach(function (sequence) {
|
||||||
|
var option = document.createElement("option");
|
||||||
|
option.value = sequence.id;
|
||||||
|
option.textContent = sequence.name + " · " + formatSequenceDuration(sequence.duration) +
|
||||||
|
" · V" + sequence.videoTracks + " / A" + sequence.audioTracks +
|
||||||
|
(sequence.active ? " · ativa" : "");
|
||||||
|
option.dataset.sequence = JSON.stringify(sequence);
|
||||||
|
select.appendChild(option);
|
||||||
|
});
|
||||||
|
var activeIndex = -1;
|
||||||
|
for (var sequenceIndex = 0; sequenceIndex < result.sequences.length; sequenceIndex++) {
|
||||||
|
if (result.sequences[sequenceIndex].active) {
|
||||||
|
activeIndex = sequenceIndex;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
select.selectedIndex = activeIndex >= 0 ? activeIndex : 0;
|
||||||
|
picker.hidden = false;
|
||||||
|
document.getElementById("silenceSequenceListInfo").textContent =
|
||||||
|
result.sequences.length + " timeline(s) encontrada(s).";
|
||||||
|
silenceSelectSequence();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function silenceSelectSequence() {
|
||||||
|
var select = document.getElementById("silenceSequenceSelect");
|
||||||
|
var option = select && select.options[select.selectedIndex];
|
||||||
|
if (!option || !option.dataset.sequence) return;
|
||||||
|
var sequence = JSON.parse(option.dataset.sequence);
|
||||||
|
setBusy("btnDetectClip", true);
|
||||||
|
setStep(1, "running", "Lendo timeline…");
|
||||||
|
document.getElementById("silenceClipInfo").textContent = "Lendo o primeiro clipe da timeline escolhida…";
|
||||||
|
var sequenceId = JSON.stringify(String(sequence.id));
|
||||||
|
cs.evalScript(
|
||||||
|
"(function(){" +
|
||||||
|
"try{" +
|
||||||
|
"var sequenceId=" + sequenceId + ";" +
|
||||||
|
"var seq=null;" +
|
||||||
|
"for(var i=0;i<app.project.sequences.numSequences;i++){if(String(app.project.sequences[i].sequenceID)===sequenceId){seq=app.project.sequences[i];break;}}" +
|
||||||
|
"if(!seq) return JSON.stringify({ok:false,error:'Timeline não encontrada.'});" +
|
||||||
"var vt=seq.videoTracks[0];" +
|
"var vt=seq.videoTracks[0];" +
|
||||||
"if(!vt||vt.clips.numItems===0) return JSON.stringify({ok:false,error:'Vídeo 1 está vazio na sequência ativa.'});" +
|
"if(!vt||vt.clips.numItems===0) return JSON.stringify({ok:false,error:'Vídeo 1 está vazio nesta timeline.'});" +
|
||||||
"var clip=vt.clips[0];" +
|
"var clip=vt.clips[0];" +
|
||||||
"var projItem=clip.projectItem;" +
|
"var projItem=clip.projectItem;" +
|
||||||
"var mediaPath=projItem&&projItem.getMediaPath?projItem.getMediaPath():'';" +
|
"var mediaPath=projItem&&projItem.getMediaPath?projItem.getMediaPath():'';" +
|
||||||
"return JSON.stringify({ok:true,sequenceName:seq.name,mediaPath:mediaPath,clipName:clip.name});" +
|
"return JSON.stringify({ok:true,sequenceId:String(seq.sequenceID),sequenceName:seq.name,mediaPath:mediaPath,clipName:clip.name});" +
|
||||||
"}catch(e){return JSON.stringify({ok:false,error:String(e)});}" +
|
"}catch(e){return JSON.stringify({ok:false,error:String(e)});}" +
|
||||||
"}())",
|
"}())",
|
||||||
function (raw) {
|
function (raw) {
|
||||||
@@ -1471,20 +1560,23 @@ function silenceDetectClip() {
|
|||||||
|
|
||||||
if (!result.ok || !result.mediaPath) {
|
if (!result.ok || !result.mediaPath) {
|
||||||
card.hidden = true;
|
card.hidden = true;
|
||||||
setStep(1, "error", "Não encontrado");
|
setStep(1, "error", "Sem mídia");
|
||||||
info.textContent = (result.error || "Caminho de mídia vazio.") +
|
info.textContent = result.error || "Caminho de mídia vazio.";
|
||||||
" Abra a sequência no Premiere e tente de novo.";
|
showToast("err", info.textContent);
|
||||||
showToast("err", result.error || "Não foi possível ler a sequência ativa.");
|
|
||||||
lockStepsFrom(2);
|
lockStepsFrom(2);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
silenceState.sequenceId = result.sequenceId;
|
||||||
silenceState.sequenceName = result.sequenceName;
|
silenceState.sequenceName = result.sequenceName;
|
||||||
silenceState.mediaPath = result.mediaPath;
|
silenceState.mediaPath = result.mediaPath;
|
||||||
silenceState.fileBaseName = slugify(result.clipName || result.sequenceName);
|
silenceState.fileBaseName = slugify(result.clipName || result.sequenceName);
|
||||||
silenceState.outputDir = path.join(SILENCE_OUTPUT_ROOT, silenceState.fileBaseName);
|
silenceState.outputDir = path.join(SILENCE_OUTPUT_ROOT, silenceState.fileBaseName);
|
||||||
silenceState.transcriptPath = null;
|
silenceState.transcriptPath = null;
|
||||||
silenceState.jsonOutputPath = null;
|
silenceState.jsonOutputPath = null;
|
||||||
|
silenceState.speakers = [];
|
||||||
|
var speakerConfig = document.getElementById("silenceSpeakerConfig");
|
||||||
|
if (speakerConfig) speakerConfig.hidden = true;
|
||||||
|
|
||||||
document.getElementById("clipSequenceName").textContent = result.sequenceName;
|
document.getElementById("clipSequenceName").textContent = result.sequenceName;
|
||||||
document.getElementById("clipFileName").textContent = path.basename(result.mediaPath);
|
document.getElementById("clipFileName").textContent = path.basename(result.mediaPath);
|
||||||
@@ -1494,18 +1586,22 @@ function silenceDetectClip() {
|
|||||||
card.hidden = false;
|
card.hidden = false;
|
||||||
info.textContent = "";
|
info.textContent = "";
|
||||||
|
|
||||||
setStep(1, "done", "Vídeo escolhido");
|
setStep(1, "done", "Timeline escolhida");
|
||||||
lockStepsFrom(2);
|
lockStepsFrom(2);
|
||||||
setStep(2, "ready", "Sua vez");
|
setStep(2, "ready", "Sua vez");
|
||||||
document.getElementById("btnTranscribe").disabled = false;
|
document.getElementById("btnTranscribe").disabled = false;
|
||||||
document.getElementById("silenceJsonCard").hidden = true;
|
document.getElementById("silenceJsonCard").hidden = true;
|
||||||
document.getElementById("silencePlanInfo").textContent = "";
|
document.getElementById("silencePlanInfo").textContent = "";
|
||||||
silenceLog("Clipe detectado: " + result.mediaPath);
|
silenceLog("Timeline escolhida: " + result.sequenceName + " — " + result.mediaPath);
|
||||||
renderCachedTranscripts();
|
renderCachedTranscripts();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function silenceDetectClip() {
|
||||||
|
silenceListSequences();
|
||||||
|
}
|
||||||
|
|
||||||
// Volta os passos seguintes ao estado travado quando o vídeo (ou a transcrição)
|
// Volta os passos seguintes ao estado travado quando o vídeo (ou a transcrição)
|
||||||
// muda — evita aplicar um plano gerado para outro material.
|
// muda — evita aplicar um plano gerado para outro material.
|
||||||
function lockStepsFrom(first) {
|
function lockStepsFrom(first) {
|
||||||
@@ -1554,6 +1650,7 @@ function silenceUseCachedTranscript() {
|
|||||||
silenceState.transcriptPath = entry.transcriptPath;
|
silenceState.transcriptPath = entry.transcriptPath;
|
||||||
silenceState.transcribeModel = entry.model;
|
silenceState.transcribeModel = entry.model;
|
||||||
silenceState.cachedTranscriptPath = entry.transcriptPath;
|
silenceState.cachedTranscriptPath = entry.transcriptPath;
|
||||||
|
silenceLoadSpeakerConfiguration();
|
||||||
document.getElementById("silenceCacheInfo").textContent =
|
document.getElementById("silenceCacheInfo").textContent =
|
||||||
"Usando a transcrição salva em " + new Date(entry.savedAt).toLocaleString() + ".";
|
"Usando a transcrição salva em " + new Date(entry.savedAt).toLocaleString() + ".";
|
||||||
unlockAfterTranscript();
|
unlockAfterTranscript();
|
||||||
@@ -1676,6 +1773,9 @@ function silenceTranscribe() {
|
|||||||
}
|
}
|
||||||
silenceState.transcriptPath = out;
|
silenceState.transcriptPath = out;
|
||||||
silenceState.transcribeModel = model;
|
silenceState.transcribeModel = model;
|
||||||
|
silenceState.speakers = [];
|
||||||
|
var speakerConfig = document.getElementById("silenceSpeakerConfig");
|
||||||
|
if (speakerConfig) speakerConfig.hidden = true;
|
||||||
var resumo = parsed.segments + " trechos · " + Math.round(parsed.duration) + "s de áudio";
|
var resumo = parsed.segments + " trechos · " + Math.round(parsed.duration) + "s de áudio";
|
||||||
silenceLog("Transcrição concluída: " + resumo, "ok");
|
silenceLog("Transcrição concluída: " + resumo, "ok");
|
||||||
document.getElementById("silenceCacheInfo").textContent = "Transcrição pronta — " + resumo + ".";
|
document.getElementById("silenceCacheInfo").textContent = "Transcrição pronta — " + resumo + ".";
|
||||||
@@ -1734,6 +1834,7 @@ 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);
|
||||||
var msg = parsed ? parsed.num_speakers + " locutor(es): " + parsed.speakers.join(", ") : "Locutores detectados.";
|
var msg = parsed ? parsed.num_speakers + " locutor(es): " + parsed.speakers.join(", ") : "Locutores detectados.";
|
||||||
silenceLog(msg, "ok");
|
silenceLog(msg, "ok");
|
||||||
info.textContent = msg;
|
info.textContent = msg;
|
||||||
@@ -1745,6 +1846,88 @@ function silenceDiarizeSpeakers() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function speakerNameDefault(speakerId) {
|
||||||
|
return speakerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderSpeakerConfiguration(speakerIds) {
|
||||||
|
var config = document.getElementById("silenceSpeakerConfig");
|
||||||
|
var list = document.getElementById("silenceSpeakerList");
|
||||||
|
var count = document.getElementById("silenceSpeakerCount");
|
||||||
|
if (!config || !list || !count) return;
|
||||||
|
var previous = {};
|
||||||
|
silenceState.speakers.forEach(function (speaker) { previous[speaker.id] = speaker; });
|
||||||
|
silenceState.speakers = speakerIds.map(function (id) {
|
||||||
|
var old = previous[id];
|
||||||
|
return {
|
||||||
|
id: id,
|
||||||
|
name: old && old.name ? old.name : speakerNameDefault(id),
|
||||||
|
active: !old || old.active !== false,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
count.textContent = silenceState.speakers.length + (silenceState.speakers.length === 1 ? " identificado" : " identificados");
|
||||||
|
list.innerHTML = "";
|
||||||
|
silenceState.speakers.forEach(function (speaker, index) {
|
||||||
|
var row = document.createElement("div");
|
||||||
|
row.className = "speaker-row";
|
||||||
|
var label = document.createElement("label");
|
||||||
|
label.className = "field-label";
|
||||||
|
label.textContent = speaker.id;
|
||||||
|
var input = document.createElement("input");
|
||||||
|
input.type = "text";
|
||||||
|
input.className = "select-field speaker-name-input";
|
||||||
|
input.value = speaker.name;
|
||||||
|
input.maxLength = 120;
|
||||||
|
input.setAttribute("aria-label", "Nome de " + speaker.id);
|
||||||
|
input.addEventListener("input", function () { silenceState.speakers[index].name = input.value.trim() || speaker.id; });
|
||||||
|
var activeLabel = document.createElement("label");
|
||||||
|
activeLabel.className = "checkbox-field speaker-active-field";
|
||||||
|
var active = document.createElement("input");
|
||||||
|
active.type = "checkbox";
|
||||||
|
active.checked = speaker.active;
|
||||||
|
active.addEventListener("change", function () { silenceState.speakers[index].active = active.checked; });
|
||||||
|
var activeText = document.createElement("span");
|
||||||
|
activeText.textContent = "Incluir no vídeo";
|
||||||
|
activeLabel.appendChild(active);
|
||||||
|
activeLabel.appendChild(activeText);
|
||||||
|
row.appendChild(label);
|
||||||
|
row.appendChild(input);
|
||||||
|
row.appendChild(activeLabel);
|
||||||
|
list.appendChild(row);
|
||||||
|
});
|
||||||
|
config.hidden = silenceState.speakers.length === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function silenceLoadSpeakerConfiguration() {
|
||||||
|
if (!silenceState.transcriptPath) return;
|
||||||
|
try {
|
||||||
|
var transcript = JSON.parse(fs.readFileSync(silenceState.transcriptPath, "utf-8"));
|
||||||
|
var diarization = transcript.diarization;
|
||||||
|
if (diarization && Array.isArray(diarization.speakers)) renderSpeakerConfiguration(diarization.speakers);
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function speakerConfigurationForJson() {
|
||||||
|
var speakers = silenceState.speakers.map(function (speaker) {
|
||||||
|
var active = speaker.active !== false;
|
||||||
|
return {
|
||||||
|
id: speaker.id,
|
||||||
|
name: speaker.name || speaker.id,
|
||||||
|
status: active ? "active" : "inactive",
|
||||||
|
include_in_video: active,
|
||||||
|
editorial_instruction: active
|
||||||
|
? "Manter as falas deste falante, salvo decisão específica da IA."
|
||||||
|
: "Excluir todas as falas deste falante do vídeo final.",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
total_detected: speakers.length,
|
||||||
|
speakers: speakers,
|
||||||
|
inactive_speakers: speakers.filter(function (speaker) { return !speaker.include_in_video; }).map(function (speaker) { return speaker.id; }),
|
||||||
|
instruction: "Falantes inativos devem ter todas as suas falas removidas do vídeo final; não confundir nome com identidade confirmada.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Depois de mutar silenceState.transcriptPath em lugar (diarização, métricas de
|
// Depois de mutar silenceState.transcriptPath em lugar (diarização, métricas de
|
||||||
// voz), propaga a mudança pra cópia central de cache, se forem arquivos diferentes.
|
// voz), propaga a mudança pra cópia central de cache, se forem arquivos diferentes.
|
||||||
function syncTranscriptToCache() {
|
function syncTranscriptToCache() {
|
||||||
@@ -1818,6 +2001,7 @@ function silenceGenerateJson() {
|
|||||||
transcript.settings = Object.assign({}, transcript.settings, {
|
transcript.settings = Object.assign({}, transcript.settings, {
|
||||||
silence_removal: { enabled: silenceEnabled, min_silence_seconds: minSilence },
|
silence_removal: { enabled: silenceEnabled, min_silence_seconds: minSilence },
|
||||||
});
|
});
|
||||||
|
transcript.speaker_configuration = speakerConfigurationForJson();
|
||||||
|
|
||||||
ensureDir(silenceState.outputDir);
|
ensureDir(silenceState.outputDir);
|
||||||
var out = path.join(silenceState.outputDir, "dados-para-ia.json");
|
var out = path.join(silenceState.outputDir, "dados-para-ia.json");
|
||||||
@@ -1937,7 +2121,7 @@ function applyEditorialActions() {
|
|||||||
silenceLog("Aplicando plano de edição na timeline…");
|
silenceLog("Aplicando plano de edição na timeline…");
|
||||||
runStreamed(
|
runStreamed(
|
||||||
nodeBin,
|
nodeBin,
|
||||||
[applyScript, "--plan", planPath, "--sequence", silenceState.sequenceName],
|
[applyScript, "--plan", planPath, "--sequence", silenceState.sequenceId || silenceState.sequenceName],
|
||||||
function (line) { silenceLog(line); },
|
function (line) { silenceLog(line); },
|
||||||
function (code, lastErr) {
|
function (code, lastErr) {
|
||||||
setBusy("btnApplyEditorialActions", false);
|
setBusy("btnApplyEditorialActions", false);
|
||||||
|
|||||||
@@ -388,6 +388,8 @@ textarea { padding: 8px 10px; font: 10px var(--font-mono); color: var(--text-sec
|
|||||||
.kv dt { flex: 0 0 62px; color: var(--text-muted); font-size: 9px; }
|
.kv dt { flex: 0 0 62px; color: var(--text-muted); font-size: 9px; }
|
||||||
.kv dd { flex: 1; min-width: 0; overflow: hidden; margin: 0; color: var(--text); font-size: 10px; text-overflow: ellipsis; white-space: nowrap; }
|
.kv dd { flex: 1; min-width: 0; overflow: hidden; margin: 0; color: var(--text); font-size: 10px; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
.kv .kv-path { color: var(--text-muted); font: 9px var(--font-mono); }
|
.kv .kv-path { color: var(--text-muted); font: 9px var(--font-mono); }
|
||||||
|
.sequence-picker { margin-top: 10px; }
|
||||||
|
.sequence-picker[hidden] { display: none; }
|
||||||
|
|
||||||
.stat-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
|
.stat-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
|
||||||
.stat-row[hidden] { display: none; }
|
.stat-row[hidden] { display: none; }
|
||||||
@@ -400,6 +402,14 @@ textarea { padding: 8px 10px; font: 10px var(--font-mono); color: var(--text-sec
|
|||||||
.mini-action strong { display: block; font-size: 10.5px; font-weight: 600; }
|
.mini-action strong { display: block; font-size: 10.5px; font-weight: 600; }
|
||||||
.mini-action span { display: block; margin-top: 3px; color: var(--text-muted); font-size: 9.2px; line-height: 1.4; }
|
.mini-action span { display: block; margin-top: 3px; color: var(--text-muted); font-size: 9.2px; line-height: 1.4; }
|
||||||
.mini-action .button { width: auto; flex: 0 0 auto; padding: 0 12px; height: 28px; }
|
.mini-action .button { width: auto; flex: 0 0 auto; padding: 0 12px; height: 28px; }
|
||||||
|
.speaker-config { margin: 8px 0; padding: 10px; border: 1px solid var(--border); border-radius: 6px; background: var(--surface-deep); }
|
||||||
|
.speaker-config-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 10.5px; }
|
||||||
|
.speaker-config-head span { color: var(--text-muted); font-size: 9.2px; }
|
||||||
|
.speaker-config .field-help { margin: 6px 0 8px; }
|
||||||
|
.speaker-row { display: grid; grid-template-columns: 92px minmax(0, 1fr) auto; align-items: center; gap: 8px; padding: 6px 0; border-top: 1px solid var(--border); }
|
||||||
|
.speaker-row .field-label { margin: 0; }
|
||||||
|
.speaker-name-input { width: 100%; }
|
||||||
|
.speaker-active-field { margin: 0; padding: 6px 8px; white-space: nowrap; }
|
||||||
|
|
||||||
/* ------------------------------- Callouts ------------------------------- */
|
/* ------------------------------- Callouts ------------------------------- */
|
||||||
.callout { padding: 10px 11px; border: 1px solid var(--border); border-left-width: 3px; border-radius: 6px; background: var(--surface-deep); }
|
.callout { padding: 10px 11px; border: 1px solid var(--border); border-left-width: 3px; border-radius: 6px; background: var(--surface-deep); }
|
||||||
@@ -622,4 +632,3 @@ button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-
|
|||||||
.analise-fala strong { display: inline-block; min-width: 34px; color: var(--text-muted); font-weight: 600; text-transform: uppercase; }
|
.analise-fala strong { display: inline-block; min-width: 34px; color: var(--text-muted); font-weight: 600; text-transform: uppercase; }
|
||||||
.analise-fala.analise-vazia { color: var(--text-muted); font-style: italic; }
|
.analise-fala.analise-vazia { color: var(--text-muted); font-style: italic; }
|
||||||
.analise-evidencias { margin: 6px 0 0; font-size: 10px; color: var(--text-muted); }
|
.analise-evidencias { margin: 6px 0 0; font-size: 10px; color: var(--text-muted); }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user