feat: criada a aba Tipos de vídeo com cinco perfis editoriais pré-

- criada a aba Tipos de vídeo com cinco perfis editoriais pré-cadastrados e catálogo local pronto para integração com o Schedule Light
- ampliado o cadastro vertical dos perfis com objetivos, narrativa, emoção, prioridades, formato, áudio e regras para IA
- criado plano JSON para resumir a entrevista jornalística em até três minutos

Resumo:
- 4 arquivos alterados
- 1 novos
- 3 modificados
- 0 removidos

 3 files changed, 318 insertions(+), 3 deletions(-)

Arquivos:
  - code/cep-plugin/index.html
  - code/cep-plugin/main.js
  - code/cep-plugin/styles.css
  - plano-resumo-video.json
This commit is contained in:
João Henrique
2026-09-09 11:25:11 -04:00
parent d37143d733
commit 6e1bd052fe
4 changed files with 418 additions and 3 deletions
+213
View File
@@ -30,6 +30,28 @@ function log(msg, cls) {
// reposicionado na aba de origem com etapa atual, barra e tempo decorrido.
var taskState = { active: false, startedAt: 0, timer: null, clearTimer: null, mirror: null, originTab: "silence", originPanelId: "tabSilence", errorText: "" };
var TIPOS_VIDEO_CHAVE = "schedule_light_tipos_video";
var tipoVideoAtualId = null;
var tiposVideoPadrao = [
{ id: "midia-social", nome: "Conteúdo para mídia social", duracao_maxima_segundos: 60, descricao: "Vídeo curto para interromper a rolagem, comunicar uma ideia e estimular uma ação.", canais: "Instagram Reels, TikTok, Shorts", proporcao: "9:16", ritmo: "agil", emocao: "Curiosidade e energia", storytelling: true, estrutura: "Gancho nos primeiros 2 segundos → desenvolvimento em blocos curtos → CTA direto.", cortes: "Cortes rápidos, remover pausas e repetições; preservar frases de impacto. Usar jump cuts com moderação.", broll: "Inserir imagens de apoio a cada mudança de ideia, preferindo planos que reforcem a fala.", texto: "Legendas sempre; destacar palavras-chave na tela e usar título curto. CTA visual no final.", som: "Voz sempre inteligível e à frente da trilha; música energética em volume baixo.", diretriz: "Priorize retenção e clareza. Cada corte deve manter o ritmo e reforçar uma única ideia por vez." },
{ id: "depoimento", nome: "Depoimento de cliente", duracao_maxima_segundos: 120, descricao: "Relato autêntico que gera identificação, confiança e prova social.", canais: "Site, redes sociais, apresentação comercial", proporcao: "adaptavel", ritmo: "equilibrado", emocao: "Empatia e confiança", storytelling: true, estrutura: "Problema vivido → experiência com a solução → transformação → recomendação.", cortes: "Cortar hesitações e desvios sem deixar a fala artificial; manter reações, pausas emocionais e frases espontâneas.", broll: "Cobrir cortes com cenas do cliente usando o produto, ambiente real ou detalhes que comprovem o relato.", texto: "Nome e contexto do depoente; destacar a transformação em frases curtas; legendas completas.", som: "Voz natural, limpa e próxima; trilha emocional discreta, crescendo apenas na transformação.", diretriz: "Proteja a autenticidade do depoimento. A emoção deve vir da fala e das evidências, não de efeitos excessivos." },
{ id: "entrevista", nome: "Entrevista", duracao_maxima_segundos: 600, descricao: "Conversa estruturada para extrair conhecimento, opinião ou história de uma pessoa.", canais: "YouTube, podcast em vídeo, cortes sociais", proporcao: "16:9", ritmo: "equilibrado", emocao: "Interesse e credibilidade", storytelling: true, estrutura: "Apresentação → perguntas essenciais → aprofundamento → síntese e encerramento.", cortes: "Remover redundâncias e silêncios longos; preservar respostas completas, escuta, reação e contexto da pergunta.", broll: "Usar planos de reação, detalhes do ambiente e imagens relacionadas ao tema para quebrar a monotonia.", texto: "Identificação do entrevistado, perguntas resumidas quando necessário, capítulos ou palavras-chave.", som: "Diálogo consistente entre os canais; reduzir ruído e nivelar vozes sem tirar naturalidade.", diretriz: "Mantenha a linha de raciocínio da conversa. Não cortar uma resposta de modo que altere o sentido ou a intenção do entrevistado." },
{ id: "institucional", nome: "Vídeo institucional", duracao_maxima_segundos: 180, descricao: "Apresentação da empresa, marca, cultura ou solução com percepção de valor.", canais: "Site, eventos, apresentações, LinkedIn", proporcao: "16:9", ritmo: "progressivo", emocao: "Segurança e inspiração", storytelling: true, estrutura: "Contexto ou propósito → pessoas e operação → diferenciais → prova → convite para conhecer.", cortes: "Ritmo elegante; evitar excesso de cortes. Alternar entrevistas, planos de ambiente e detalhes de operação.", broll: "B-roll planejado para mostrar pessoas, processos, produto e escala; toda imagem deve provar uma mensagem.", texto: "Títulos limpos, dados essenciais e assinatura da marca; evitar excesso de texto na tela.", som: "Trilha cinematográfica ou institucional discreta; voz firme, clara e com pausas bem preservadas.", diretriz: "Construa confiança e percepção de valor. Use consistência visual e sonora; efeitos só quando ajudarem a mensagem da marca." },
{ id: "educativo-storytelling", nome: "Educativo com storytelling", duracao_maxima_segundos: 240, descricao: "Explicação didática que ensina uma ideia por meio de uma história, caso ou problema concreto.", canais: "YouTube, curso, LinkedIn, redes sociais", proporcao: "adaptavel", ritmo: "progressivo", emocao: "Curiosidade e clareza", storytelling: true, estrutura: "Problema ou pergunta → história/exemplo → explicação → aplicação prática → resumo.", cortes: "Cortar repetições, mas manter a sequência pedagógica; inserir pausas após conceitos importantes.", broll: "Usar diagramas, tela, exemplos e imagens que tornem o conceito visível; sincronizar apoio com a explicação.", texto: "Título do conceito, palavras-chave e resumo final; legendas e marcadores de etapas.", som: "Voz em primeiro plano; trilha quase imperceptível; pequenos efeitos apenas para sinalizar transições.", diretriz: "Ensine uma coisa por vez. O espectador deve entender o conceito sem depender apenas de texto ou de efeitos visuais." }
];
function tipoVideoCompletar(tipo) {
var configuracoes = {
"midia-social": { categoria: "Comercial", objetivo: "Gerar retenção e ação", objetivo_secundario: "Comunicar uma ideia rapidamente", publico: "Público das redes sociais", mensagem: "Uma ideia clara por vídeo.", tipo_narrativa: "Problema → solução → benefício", foco_narrativo: "Gancho e CTA", intensidade: 4, tom: "Positivo", construcao_emocao: "Ritmo ágil, cortes de impacto, legendas e trilha energética.", duracao_minima: 15, prioridades: ["Retenção", "Clareza", "Mensagem", "Ritmo", "Estética"], usa_broll: true, usa_legendas: true, usa_textos: true, usa_graficos: false, usa_zoom: true, usa_musica: true, intensidade_musica: 25, prioridade_voz: "Muito alta", restricoes: "Evitar excesso de informação e cortes sem função.", qualidade: "A abertura prende atenção e a mensagem é compreendida sem áudio." },
"depoimento": { categoria: "Depoimento", objetivo: "Gerar credibilidade e confiança", objetivo_secundario: "Criar identificação emocional", publico: "Clientes em potencial", mensagem: "A experiência real comprova o valor da solução.", tipo_narrativa: "História pessoal", foco_narrativo: "Problema → experiência → transformação → recomendação", intensidade: 3, tom: "Íntimo", construcao_emocao: "Preservar pausas naturais, reações e imagens reais; usar trilha discreta.", duracao_minima: 30, prioridades: ["Autenticidade", "Clareza", "Conexão emocional", "Ritmo", "Estética"], usa_broll: true, usa_legendas: true, usa_textos: true, usa_graficos: false, usa_zoom: false, usa_musica: true, intensidade_musica: 20, prioridade_voz: "Muito alta", restricoes: "Não alterar o sentido das frases nem deixar o depoimento artificial.", qualidade: "A pessoa parece verdadeira e o resultado do relato fica evidente." },
"entrevista": { categoria: "Entrevista", objetivo: "Transmitir conhecimento e autoridade", objetivo_secundario: "Organizar a conversa por temas", publico: "Pessoas interessadas no assunto", mensagem: "O conteúdo da conversa é compreendido com contexto.", tipo_narrativa: "Pergunta e resposta", foco_narrativo: "Pergunta → resposta → aprofundamento", intensidade: 2, tom: "Informativo", construcao_emocao: "Reações, escuta e pausas que preservem a naturalidade da conversa.", duracao_minima: 60, prioridades: ["Clareza", "Conteúdo", "Contexto", "Ritmo", "Estética"], usa_broll: true, usa_legendas: true, usa_textos: true, usa_graficos: false, usa_zoom: true, usa_musica: false, intensidade_musica: 10, prioridade_voz: "Muito alta", restricoes: "Não cortar frases de modo que prejudique o raciocínio do entrevistado.", qualidade: "As respostas mantêm contexto, lógica e informação relevante." },
"institucional": { categoria: "Institucional", objetivo: "Apresentar valor e confiança na marca", objetivo_secundario: "Mostrar pessoas, processo e diferenciais", publico: "Clientes, parceiros e equipe", mensagem: "A marca entrega valor de forma consistente.", tipo_narrativa: "Jornada / transformação", foco_narrativo: "Propósito → pessoas → operação → prova", intensidade: 4, tom: "Inspirador", construcao_emocao: "Trilha progressiva, imagens de pessoas e planos de operação.", duracao_minima: 45, prioridades: ["Confiança", "Mensagem", "Narrativa", "Estética", "Ritmo"], usa_broll: true, usa_legendas: true, usa_textos: true, usa_graficos: true, usa_zoom: false, usa_musica: true, intensidade_musica: 35, prioridade_voz: "Alta", restricoes: "Evitar efeitos decorativos que não reforcem a mensagem da marca.", qualidade: "O vídeo transmite profissionalismo, clareza e percepção de valor." },
"educativo-storytelling": { categoria: "Educativo", objetivo: "Ensinar um assunto com clareza", objetivo_secundario: "Aumentar retenção e aplicação prática", publico: "Pessoas que precisam aprender o assunto", mensagem: "O espectador consegue entender e aplicar o conceito.", tipo_narrativa: "Explicação passo a passo", foco_narrativo: "Problema → exemplo → explicação → aplicação", intensidade: 2, tom: "Informativo", construcao_emocao: "Curiosidade inicial, exemplos visuais e pausas após conceitos importantes.", duracao_minima: 60, prioridades: ["Clareza", "Organização", "Didática", "Retenção", "Estética"], usa_broll: true, usa_legendas: true, usa_textos: true, usa_graficos: true, usa_zoom: true, usa_musica: false, intensidade_musica: 5, prioridade_voz: "Muito alta", restricoes: "Não cortar exemplos ou transições que prejudiquem a explicação.", qualidade: "O conteúdo fica fácil de acompanhar, revisar e aplicar." }
}[tipo.id] || {};
var completo = Object.assign({ ativo: true, categoria: "Personalizado", objetivo: "", objetivo_secundario: "", publico: "", mensagem: "", tipo_narrativa: "História pessoal", foco_narrativo: "", intensidade: 3, tom: "Positivo", construcao_emocao: "", duracao_minima: 0, prioridades: ["Clareza", "Mensagem", "Ritmo", "Conexão emocional", "Estética"], usa_broll: false, usa_legendas: true, usa_textos: false, usa_graficos: false, usa_zoom: false, usa_musica: false, intensidade_musica: 20, prioridade_voz: "Alta", restricoes: "", qualidade: "" }, configuracoes, tipo);
return completo;
}
function taskEl(id) { return document.getElementById(id); }
function formatClock(ms) {
@@ -207,6 +229,7 @@ function switchTab(name) {
{ key: "scanner", panel: "tabScanner", btn: "tabBtnScanner" },
{ key: "retakes", panel: "tabRetakes", btn: "tabBtnRetakes" },
{ key: "analises", panel: "tabAnalises", btn: "tabBtnAnalises" },
{ key: "tipos-video", panel: "tabTiposVideo", btn: "tabBtnTiposVideo" },
{ key: "bridge", panel: "tabBridge", btn: "tabBtnBridge" },
{ key: "models", panel: "tabModels", btn: "tabBtnModels" },
{ key: "settings", panel: "tabSettings", btn: "tabBtnSettings" },
@@ -226,6 +249,196 @@ function switchTab(name) {
if (name === "scanner") { renderScannerTranscriptionOptions(); loadScannerProfile(); renderScannerHuggingFaceStatus(); }
if (name === "retakes") retakesAoAbrir();
if (name === "analises") analisesAoAbrir();
if (name === "tipos-video") tiposVideoAoAbrir();
}
// ---- Catálogo de tipos de vídeo -------------------------------------------
// A primeira versão usa o armazenamento local do painel. O documento salvo já
// tem formato estável e pode ser migrado para o Schedule Light sem alterar o
// contrato editorial enviado para a IA.
function tiposVideoLer() {
try {
var salvo = JSON.parse(localStorage.getItem(TIPOS_VIDEO_CHAVE) || "null");
if (Array.isArray(salvo) && salvo.length) return salvo.map(tipoVideoCompletar);
var iniciais = tiposVideoPadrao.slice().map(tipoVideoCompletar);
tiposVideoSalvarNoCatalogo(iniciais);
return iniciais;
} catch (_) { return tiposVideoPadrao.slice().map(tipoVideoCompletar); }
}
function tiposVideoSalvarNoCatalogo(lista) {
localStorage.setItem(TIPOS_VIDEO_CHAVE, JSON.stringify(lista));
}
function tiposVideoAoAbrir() {
tiposVideoRenderizarLista();
if (!tipoVideoAtualId) tipoVideoNovo();
}
function tiposVideoRenderizarLista() {
var lista = document.getElementById("tiposVideoLista");
if (!lista) return;
lista.innerHTML = "";
tiposVideoLer().forEach(function (tipo) {
var item = document.createElement("div");
item.className = "video-type-row" + (tipo.id === tipoVideoAtualId ? " is-selected" : "");
item.setAttribute("role", "listitem");
item.innerHTML = '<div class="video-type-copy"><strong></strong><span></span></div><div class="video-type-meta"></div><button class="button button-ghost" type="button">Editar</button>';
item.querySelector("strong").textContent = tipo.nome;
item.querySelector("span").textContent = tipo.descricao;
item.querySelector(".video-type-meta").textContent = tipo.duracao_maxima_segundos + " s · " + tipo.proporcao + " · " + tipo.canais;
item.querySelector("button").onclick = function () { tipoVideoEditar(tipo.id); };
lista.appendChild(item);
});
}
function tipoVideoNovo() {
tipoVideoAtualId = null;
tipoVideoPreencher(tipoVideoCompletar({ id: "", nome: "", duracao_maxima_segundos: 60, descricao: "", canais: "", proporcao: "9:16", ritmo: "equilibrado", emocao: "", storytelling: true, estrutura: "", cortes: "", broll: "", texto: "", som: "", diretriz: "" }));
document.getElementById("tipoVideoEditorTitulo").textContent = "Novo tipo de vídeo";
document.getElementById("tipoVideoEditorStatus").textContent = "Rascunho";
document.getElementById("tipoVideoEditorCard").hidden = false;
tiposVideoRenderizarLista();
}
function tipoVideoEditar(id) {
var tipo = tiposVideoLer().filter(function (item) { return item.id === id; })[0];
if (!tipo) return;
tipoVideoAtualId = id;
tipoVideoPreencher(tipo);
document.getElementById("tipoVideoEditorTitulo").textContent = "Editar tipo de vídeo";
document.getElementById("tipoVideoEditorStatus").textContent = "Cadastrado";
document.getElementById("tipoVideoEditorCard").hidden = false;
tiposVideoRenderizarLista();
}
function tipoVideoPreencher(tipo) {
tipo = tipoVideoCompletar(tipo);
["nome", "descricao", "canais", "proporcao", "ritmo", "emocao", "estrutura", "cortes", "broll", "texto", "som", "diretriz", "categoria", "objetivo", "objetivo_secundario", "publico", "mensagem", "tipo_narrativa", "foco_narrativo", "tom", "construcao_emocao", "restricoes", "qualidade", "prioridade_voz"].forEach(function (campo) {
var elemento = document.getElementById("tipoVideo" + campo.charAt(0).toUpperCase() + campo.slice(1));
if (elemento) elemento.value = tipo[campo] || "";
});
document.getElementById("tipoVideoDuracao").value = tipo.duracao_maxima_segundos || "";
document.getElementById("tipoVideoDuracaoMinima").value = tipo.duracao_minima || "";
document.getElementById("tipoVideoStorytelling").checked = !!tipo.storytelling;
document.getElementById("tipoVideoAtivo").checked = tipo.ativo !== false;
document.getElementById("tipoVideoIntensidade").value = tipo.intensidade || 3;
document.getElementById("tipoVideoIntensidadeMusica").value = tipo.intensidade_musica || 0;
document.getElementById("tipoVideoUsaBroll").checked = !!tipo.usa_broll;
document.getElementById("tipoVideoUsaLegendas").checked = !!tipo.usa_legendas;
document.getElementById("tipoVideoUsaTextos").checked = !!tipo.usa_textos;
document.getElementById("tipoVideoUsaGraficos").checked = !!tipo.usa_graficos;
document.getElementById("tipoVideoUsaZoom").checked = !!tipo.usa_zoom;
document.getElementById("tipoVideoUsaMusica").checked = !!tipo.usa_musica;
tipoVideoRenderizarPrioridades(tipo.prioridades);
tipoVideoAtualizarIntensidade();
tipoVideoAtualizarIntensidadeMusica();
}
function tipoVideoRenderizarPrioridades(prioridades) {
var lista = document.getElementById("tipoVideoPrioridades");
lista.innerHTML = "";
(prioridades || []).slice(0, 5).forEach(function (valor, indice) {
var linha = document.createElement("div");
linha.className = "priority-row";
linha.innerHTML = '<span class="priority-number">' + (indice + 1) + '</span><input class="text-field priority-input" type="text" value="">';
linha.querySelector("input").value = valor;
lista.appendChild(linha);
});
}
function tipoVideoPrioridadesDoFormulario() {
return Array.prototype.slice.call(document.querySelectorAll("#tipoVideoPrioridades .priority-input")).map(function (input) { return input.value.trim(); }).filter(Boolean);
}
function tipoVideoAtualizarIntensidade() {
var valor = document.getElementById("tipoVideoIntensidade");
var saida = document.getElementById("tipoVideoIntensidadeValor");
if (valor && saida) saida.textContent = valor.value + " / 5";
}
function tipoVideoAtualizarIntensidadeMusica() {
var valor = document.getElementById("tipoVideoIntensidadeMusica");
var saida = document.getElementById("tipoVideoIntensidadeMusicaValor");
if (valor && saida) saida.textContent = valor.value + "%";
}
function tipoVideoDoFormulario() {
var nome = document.getElementById("tipoVideoNome").value.trim();
if (!nome) throw new Error("Informe o nome do tipo de vídeo.");
var base = tipoVideoAtualId ? tiposVideoLer().filter(function (item) { return item.id === tipoVideoAtualId; })[0] : null;
return {
id: tipoVideoAtualId || (nome.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") + "-" + Date.now()),
nome: nome,
descricao: document.getElementById("tipoVideoDescricao").value.trim(),
ativo: document.getElementById("tipoVideoAtivo").checked,
categoria: document.getElementById("tipoVideoCategoria").value,
objetivo: document.getElementById("tipoVideoObjetivo").value.trim(),
objetivo_secundario: document.getElementById("tipoVideoObjetivoSecundario").value.trim(),
publico: document.getElementById("tipoVideoPublico").value.trim(),
mensagem: document.getElementById("tipoVideoMensagem").value.trim(),
tipo_narrativa: document.getElementById("tipoVideoTipoNarrativa").value,
foco_narrativo: document.getElementById("tipoVideoFocoNarrativo").value.trim(),
intensidade: Number(document.getElementById("tipoVideoIntensidade").value) || 3,
tom: document.getElementById("tipoVideoTom").value,
construcao_emocao: document.getElementById("tipoVideoConstrucaoEmocao").value.trim(),
duracao_maxima_segundos: Number(document.getElementById("tipoVideoDuracao").value) || 0,
duracao_minima: Number(document.getElementById("tipoVideoDuracaoMinima").value) || 0,
canais: document.getElementById("tipoVideoCanais").value.trim(),
proporcao: document.getElementById("tipoVideoProporcao").value,
ritmo: document.getElementById("tipoVideoRitmo").value,
emocao: document.getElementById("tipoVideoEmocao").value.trim(),
storytelling: document.getElementById("tipoVideoStorytelling").checked,
estrutura: document.getElementById("tipoVideoEstrutura").value.trim(),
cortes: document.getElementById("tipoVideoCortes").value.trim(),
broll: document.getElementById("tipoVideoBroll").value.trim(),
texto: document.getElementById("tipoVideoTexto").value.trim(),
som: document.getElementById("tipoVideoSom").value.trim(),
diretriz: document.getElementById("tipoVideoDiretriz").value.trim(),
prioridades: tipoVideoPrioridadesDoFormulario(),
usa_broll: document.getElementById("tipoVideoUsaBroll").checked,
usa_legendas: document.getElementById("tipoVideoUsaLegendas").checked,
usa_textos: document.getElementById("tipoVideoUsaTextos").checked,
usa_graficos: document.getElementById("tipoVideoUsaGraficos").checked,
usa_zoom: document.getElementById("tipoVideoUsaZoom").checked,
usa_musica: document.getElementById("tipoVideoUsaMusica").checked,
intensidade_musica: Number(document.getElementById("tipoVideoIntensidadeMusica").value) || 0,
prioridade_voz: document.getElementById("tipoVideoPrioridadeVoz").value,
restricoes: document.getElementById("tipoVideoRestricoes").value.trim(),
qualidade: document.getElementById("tipoVideoQualidade").value.trim(),
criado_em: base && base.criado_em ? base.criado_em : new Date().toISOString(),
atualizado_em: new Date().toISOString()
};
}
function tipoVideoSalvar() {
try {
var item = tipoVideoDoFormulario();
var lista = tiposVideoLer();
var indice = lista.findIndex(function (tipo) { return tipo.id === item.id; });
if (indice >= 0) lista[indice] = item; else lista.push(item);
tiposVideoSalvarNoCatalogo(lista);
tipoVideoAtualId = item.id;
document.getElementById("tipoVideoEditorStatus").textContent = "Salvo";
document.getElementById("tiposVideoStatus").textContent = "Catálogo salvo localmente · " + lista.length + " tipo(s) cadastrado(s).";
tiposVideoRenderizarLista();
showToast("ok", "Tipo de vídeo salvo no catálogo.");
} catch (erro) { showToast("err", erro.message); }
}
function tipoVideoCancelar() {
tipoVideoAtualId = null;
document.getElementById("tipoVideoEditorCard").hidden = true;
tiposVideoRenderizarLista();
}
function tipoVideoCopiarJson() {
try {
var json = JSON.stringify({ tipo_de_video: tipoVideoDoFormulario() }, null, 2);
var copia = childProcess.spawn("pbcopy");
copia.stdin.end(json);
showToast("ok", "Configuração copiada em JSON para a IA.");
} catch (erro) { showToast("err", erro.message); }
}
// ---- Análises ---------------------------------------------------------------