primocode 8.42.2 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -37
- package/bin/primocode.js +598 -360
- package/lib/acervo.js +8 -1
- package/lib/act.js +205 -114
- package/lib/api.js +35 -4
- package/lib/boas-vindas.js +31 -192
- package/lib/catalogo.js +60 -60
- package/lib/config.js +1 -1
- package/lib/conta.js +3 -24
- package/lib/diff.js +161 -0
- package/lib/diretor.js +37 -47
- package/lib/entrada.js +545 -0
- package/lib/especialistas.js +2 -2
- package/lib/estudio/assets.js +343 -0
- package/lib/estudio/identidade.js +560 -0
- package/lib/estudio/index.js +565 -0
- package/lib/estudio/plano.js +327 -0
- package/lib/estudio/projeto.js +246 -0
- package/lib/estudio/remotion.js +606 -0
- package/lib/estudio/slides.js +190 -0
- package/lib/estudio/video.js +296 -0
- package/lib/fala.js +13 -1
- package/lib/marca.js +4 -1
- package/lib/marketplace.js +189 -0
- package/lib/permissao.js +137 -0
- package/lib/registro.js +229 -0
- package/lib/skills.js +329 -0
- package/lib/tools.js +51 -181
- package/lib/ui.js +7 -3
- package/lib/voz.js +239 -0
- package/package.json +12 -3
- package/skills/acessibilidade/SKILL.md +28 -0
- package/skills/api-que-nao-quebra/SKILL.md +27 -0
- package/skills/depurar-de-verdade/SKILL.md +29 -0
- package/skills/editar-meu-video/SKILL.md +59 -0
- package/skills/escrever-em-portugues/SKILL.md +28 -0
- package/skills/git-limpo/SKILL.md +28 -0
- package/skills/motion-de-primeira/SKILL.md +62 -0
- package/skills/pagina-que-converte/SKILL.md +28 -0
- package/skills/performance-web/SKILL.md +28 -0
- package/skills/revisao-antes-de-entregar/SKILL.md +27 -0
- package/skills/slides-que-prendem/SKILL.md +40 -0
- package/studio/edge.py +16 -66
- package/studio/narrar.py +274 -0
- package/studio/pessoa.py +167 -0
- package/voz/primo_voz/__init__.py +0 -0
- package/voz/primo_voz/__main__.py +325 -0
- package/voz/primo_voz/escuta.py +235 -0
- package/voz/primo_voz/orbe.py +216 -0
- package/lib/audio-livre.js +0 -177
- package/lib/chaves.js +0 -149
- package/lib/claude-engine.js +0 -631
- package/lib/codex-engine.js +0 -208
- package/lib/remotion.js +0 -663
- package/lib/studio.js +0 -960
- package/studio/PRIMOCODE.md +0 -58
- package/studio/README.md +0 -154
- package/studio/__pycache__/edge.cpython-311.pyc +0 -0
- package/studio/__pycache__/elevenlabs.cpython-311.pyc +0 -0
- package/studio/__pycache__/voz.cpython-311.pyc +0 -0
- package/studio/arquivos/corte-teste-de-camera-fb823.json +0 -57
- package/studio/ferramentas/00-protocolo.md +0 -125
- package/studio/ferramentas/01-modelos.md +0 -270
- package/studio/ferramentas/README.md +0 -32
- package/studio/ferramentas/corte.md +0 -919
- package/studio/ferramentas/grade.md +0 -92
- package/studio/ferramentas/prisma.md +0 -107
- package/studio/ferramentas/prosa.md +0 -86
- package/studio/ferramentas/tela.md +0 -93
- package/studio/ferramentas/traco.md +0 -79
- package/studio/server.py +0 -2254
- package/studio/studio.log +0 -57
- package/studio/voz.py +0 -388
- package/studio/web/analise.html +0 -52
- package/studio/web/core.css +0 -1167
- package/studio/web/fontes/bebas.woff2 +0 -0
- package/studio/web/fontes/grotesk-700.woff2 +0 -0
- package/studio/web/fontes/inter-400.woff2 +0 -0
- package/studio/web/fontes/inter-700.woff2 +0 -0
- package/studio/web/fontes/jetbrains-400.woff2 +0 -0
- package/studio/web/fontes/playfair-700.woff2 +0 -0
- package/studio/web/fontes.css +0 -23
- package/studio/web/home.html +0 -217
- package/studio/web/js/analise.js +0 -510
- package/studio/web/js/audio.js +0 -607
- package/studio/web/js/editor.js +0 -1802
- package/studio/web/js/exportar.js +0 -222
- package/studio/web/js/ferramentas.js +0 -108
- package/studio/web/js/icones.js +0 -120
- package/studio/web/js/midia.js +0 -157
- package/studio/web/js/narracao.js +0 -325
- package/studio/web/js/player.js +0 -903
- package/studio/web/js/render-grade.js +0 -243
- package/studio/web/js/render-prosa.js +0 -104
- package/studio/web/js/render.js +0 -1852
- package/studio/web/js/sfx.js +0 -233
- package/studio/web/js/video.js +0 -464
- package/studio/web/js/voz-neural.js +0 -152
- package/studio/web/render.css +0 -1352
- package/studio/web/tf.html +0 -15
- package/studio/web/tool.html +0 -78
- package/studio/web/view.html +0 -27
package/lib/diff.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* diff.js — o que mudou no arquivo, linha a linha.
|
|
3
|
+
*
|
|
4
|
+
* ── POR QUE ISTO EXISTE ──────────────────────────────────────────────────
|
|
5
|
+
* O que a tela dizia quando o agente editava um arquivo era isto:
|
|
6
|
+
*
|
|
7
|
+
* ● Edit src/app.js
|
|
8
|
+
* ⎿ 2 trocas · +7 linhas
|
|
9
|
+
*
|
|
10
|
+
* Sete linhas a mais. Quais? A única forma de saber era abrir o arquivo
|
|
11
|
+
* depois — ou seja, o agente mexia no seu código e você via um número. É a
|
|
12
|
+
* diferença entre acompanhar e torcer.
|
|
13
|
+
*
|
|
14
|
+
* ── POR QUE NÃO UMA BIBLIOTECA ───────────────────────────────────────────
|
|
15
|
+
* O PrimoCode não tem dependência nenhuma, e é isso que faz o `npm i -g`
|
|
16
|
+
* terminar em segundos. Um diff de linhas é um algoritmo de uma tela, e está
|
|
17
|
+
* escrito aqui embaixo.
|
|
18
|
+
*
|
|
19
|
+
* ── O DIFF NÃO VAI PARA O MODELO ─────────────────────────────────────────
|
|
20
|
+
* Ele é desenhado para a PESSOA. Mandá-lo de volta ao modelo dentro do
|
|
21
|
+
* resultado da tool custaria centenas de tokens por edição, num plano que é
|
|
22
|
+
* limitado por token — e o modelo acabou de escrever aquele texto, então não
|
|
23
|
+
* aprende nada relendo. Por isso quem chama guarda o desenho numa chave com
|
|
24
|
+
* `_` na frente, que o act.js tira antes de a conversa seguir.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
'use strict';
|
|
28
|
+
|
|
29
|
+
const { c } = require('./ui.js');
|
|
30
|
+
|
|
31
|
+
/* Arquivo grande não ganha diff desenhado, ganha o resumo em números.
|
|
32
|
+
*
|
|
33
|
+
* O motivo não é o custo do algoritmo: é a tela. Um diff de 400 linhas
|
|
34
|
+
* empurra para fora da vista tudo o que veio antes — inclusive o pedido da
|
|
35
|
+
* pessoa e os passos anteriores. Um bloco que esconde o resto da conversa é
|
|
36
|
+
* pior que um número. */
|
|
37
|
+
const MAX_LINHAS_ARQUIVO = 1200;
|
|
38
|
+
/* E há um segundo teto, este por memória: a tabela do LCS é uma matriz
|
|
39
|
+
(linhasA+1) × (linhasB+1) de inteiros. Com 1200 × 1200 são ~5,8 MB, o que
|
|
40
|
+
é barato. O produto é o que importa, não cada lado: 1200 linhas contra 20
|
|
41
|
+
é trivial, 1200 contra 1200 é o pior caso. */
|
|
42
|
+
const MAX_CELULAS = 1200 * 1200;
|
|
43
|
+
const CONTEXTO = 2; // linhas iguais mostradas em volta de cada mudança
|
|
44
|
+
const MAX_MOSTRADAS = 24; // teto do bloco inteiro, contando as de contexto
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* O diff de duas versões de um texto, como lista de operações.
|
|
48
|
+
*
|
|
49
|
+
* Algoritmo: maior subsequência comum de LINHAS, em programação dinâmica.
|
|
50
|
+
* É O(n×m) em tempo e memória, o que é perfeitamente aceitável até alguns
|
|
51
|
+
* milhares de linhas — e acima disso o desenho nem sai (ver acima).
|
|
52
|
+
*
|
|
53
|
+
* @returns {Array<{tipo:' '|'+'|'-', texto:string, a:number, b:number}>}
|
|
54
|
+
* `a` é a linha no ANTES, `b` no DEPOIS (1-based, 0 quando não existe).
|
|
55
|
+
*/
|
|
56
|
+
function operacoes(antes, depois) {
|
|
57
|
+
const A = String(antes ?? '').split('\n');
|
|
58
|
+
const B = String(depois ?? '').split('\n');
|
|
59
|
+
|
|
60
|
+
// A tabela do LCS: lcs[i][j] = tamanho da maior subsequência comum entre
|
|
61
|
+
// A[i..] e B[j..]. Construída de trás para frente para a leitura sair na
|
|
62
|
+
// ordem natural depois.
|
|
63
|
+
const lcs = Array.from({ length: A.length + 1 }, () => new Uint32Array(B.length + 1));
|
|
64
|
+
for (let i = A.length - 1; i >= 0; i--) {
|
|
65
|
+
for (let j = B.length - 1; j >= 0; j--) {
|
|
66
|
+
lcs[i][j] = A[i] === B[j]
|
|
67
|
+
? lcs[i + 1][j + 1] + 1
|
|
68
|
+
: Math.max(lcs[i + 1][j], lcs[i][j + 1]);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const ops = [];
|
|
73
|
+
let i = 0, j = 0;
|
|
74
|
+
while (i < A.length && j < B.length) {
|
|
75
|
+
if (A[i] === B[j]) { ops.push({ tipo: ' ', texto: A[i], a: i + 1, b: j + 1 }); i++; j++; }
|
|
76
|
+
else if (lcs[i + 1][j] >= lcs[i][j + 1]) { ops.push({ tipo: '-', texto: A[i], a: i + 1, b: 0 }); i++; }
|
|
77
|
+
else { ops.push({ tipo: '+', texto: B[j], a: 0, b: j + 1 }); j++; }
|
|
78
|
+
}
|
|
79
|
+
while (i < A.length) { ops.push({ tipo: '-', texto: A[i], a: i + 1, b: 0 }); i++; }
|
|
80
|
+
while (j < B.length) { ops.push({ tipo: '+', texto: B[j], a: 0, b: j + 1 }); j++; }
|
|
81
|
+
return ops;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Quantas linhas entraram e quantas saíram. */
|
|
85
|
+
function contar(ops) {
|
|
86
|
+
let mais = 0, menos = 0;
|
|
87
|
+
for (const o of ops) { if (o.tipo === '+') mais++; else if (o.tipo === '-') menos++; }
|
|
88
|
+
return { mais, menos };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Só os pedaços que interessam: cada mudança com algumas linhas iguais em
|
|
93
|
+
* volta. O resto do arquivo não entra — num arquivo de 500 linhas com uma
|
|
94
|
+
* troca, mostrar as 500 é esconder a troca.
|
|
95
|
+
*/
|
|
96
|
+
function pedacos(ops, contexto = CONTEXTO) {
|
|
97
|
+
const querida = new Array(ops.length).fill(false);
|
|
98
|
+
ops.forEach((o, k) => {
|
|
99
|
+
if (o.tipo === ' ') return;
|
|
100
|
+
for (let x = Math.max(0, k - contexto); x <= Math.min(ops.length - 1, k + contexto); x++) querida[x] = true;
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const blocos = [];
|
|
104
|
+
let atual = null;
|
|
105
|
+
for (let k = 0; k < ops.length; k++) {
|
|
106
|
+
if (!querida[k]) { atual = null; continue; }
|
|
107
|
+
if (!atual) { atual = []; blocos.push(atual); }
|
|
108
|
+
atual.push(ops[k]);
|
|
109
|
+
}
|
|
110
|
+
return blocos;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* O bloco de diff pronto para a tela.
|
|
115
|
+
*
|
|
116
|
+
* @param {string} antes
|
|
117
|
+
* @param {string} depois
|
|
118
|
+
* @param {object} op
|
|
119
|
+
* @param {string} op.prefixo o recuo de cada linha (vem do fala.resultado)
|
|
120
|
+
* @param {number} op.max teto de linhas desenhadas
|
|
121
|
+
* @returns {string} '' quando não vale a pena desenhar
|
|
122
|
+
*/
|
|
123
|
+
function desenhar(antes, depois, { prefixo = '', max = MAX_MOSTRADAS } = {}) {
|
|
124
|
+
const linhasA = String(antes ?? '').split('\n').length;
|
|
125
|
+
const linhasB = String(depois ?? '').split('\n').length;
|
|
126
|
+
if (Math.max(linhasA, linhasB) > MAX_LINHAS_ARQUIVO) return '';
|
|
127
|
+
if (linhasA * linhasB > MAX_CELULAS) return '';
|
|
128
|
+
if (antes === depois) return '';
|
|
129
|
+
|
|
130
|
+
const ops = operacoes(antes, depois);
|
|
131
|
+
const blocos = pedacos(ops);
|
|
132
|
+
if (!blocos.length) return '';
|
|
133
|
+
|
|
134
|
+
const out = [];
|
|
135
|
+
let desenhadas = 0;
|
|
136
|
+
let cortou = false;
|
|
137
|
+
|
|
138
|
+
for (const bloco of blocos) {
|
|
139
|
+
if (desenhadas >= max) { cortou = true; break; }
|
|
140
|
+
for (const o of bloco) {
|
|
141
|
+
if (desenhadas >= max) { cortou = true; break; }
|
|
142
|
+
// O número da linha é o do DEPOIS quando a linha existe lá; nas
|
|
143
|
+
// removidas, o do antes. Sem número, "+ const x = 1" não diz onde.
|
|
144
|
+
const n = String(o.b || o.a || '').padStart(4);
|
|
145
|
+
const texto = o.texto.length > 120 ? o.texto.slice(0, 119) + '…' : o.texto;
|
|
146
|
+
if (o.tipo === '+') out.push(prefixo + c.dim(n) + ' ' + c.ok('+ ' + texto));
|
|
147
|
+
else if (o.tipo === '-') out.push(prefixo + c.dim(n) + ' ' + c.err('- ' + texto));
|
|
148
|
+
else out.push(prefixo + c.dim(n) + ' ' + c.dim(' ' + texto));
|
|
149
|
+
desenhadas++;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (cortou) {
|
|
154
|
+
const { mais, menos } = contar(ops);
|
|
155
|
+
out.push(prefixo + c.dim(`… o bloco continua — ${mais} linha${mais === 1 ? '' : 's'} a mais `
|
|
156
|
+
+ `e ${menos} a menos, no total`));
|
|
157
|
+
}
|
|
158
|
+
return out.join('\n');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
module.exports = { operacoes, contar, pedacos, desenhar, MAX_LINHAS_ARQUIVO, MAX_CELULAS, MAX_MOSTRADAS };
|
package/lib/diretor.js
CHANGED
|
@@ -23,11 +23,19 @@
|
|
|
23
23
|
* diretor com o defeito apontado — uma vez. Aceitar "quase certo" aqui custa
|
|
24
24
|
* um vídeo de 40 segundos que dura 12.
|
|
25
25
|
*
|
|
26
|
-
* ── O
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
26
|
+
* ── O QUE O DIRETOR NÃO ESCOLHE MAIS ─────────────────────────────────────
|
|
27
|
+
* Ele escolhia o MOTOR (estúdio ou Remotion) e o TEMA, o LAYOUT e o FUNDO,
|
|
28
|
+
* cada um numa lista fechada — "meia-noite | claro | papel | neon", "capa |
|
|
29
|
+
* titulo | secao | duas-colunas". Motor não existe mais: o Remotion é o
|
|
30
|
+
* único renderizador. E as outras três listas saíram por decisão do produto:
|
|
31
|
+
*
|
|
32
|
+
* "tira os templates e os estilos prontos, eles deixam a IA preguiçosa e
|
|
33
|
+
* todo vídeo sai igual e genérico"
|
|
34
|
+
*
|
|
35
|
+
* Uma lista de seis temas é um template com outro nome — o modelo escolhe um
|
|
36
|
+
* dos seis e para de pensar em cor. No lugar delas o diretor DESCREVE: que
|
|
37
|
+
* paleta, que ar, o que está na tela, como a cena troca. Descrição não tem
|
|
38
|
+
* seis opções, e é o produtor quem compõe aquilo com os primitivos.
|
|
31
39
|
*/
|
|
32
40
|
|
|
33
41
|
'use strict';
|
|
@@ -56,32 +64,6 @@ function precisaDeProducao(texto) {
|
|
|
56
64
|
|
|
57
65
|
// ── 2. as decisões que não são do modelo ─────────────────────────────────
|
|
58
66
|
|
|
59
|
-
/**
|
|
60
|
-
* Estúdio ou Remotion.
|
|
61
|
-
*
|
|
62
|
-
* O estúdio exporta em segundos e não instala nada: é o padrão, e para story,
|
|
63
|
-
* reels e teaser é a resposta certa. Remotion custa um `npm install` de
|
|
64
|
-
* minutos e meio giga, e devolve o que o estúdio não tem: quadro a quadro
|
|
65
|
-
* determinístico, vídeo longo sem risco de engasgo, canal alfa e ProRes.
|
|
66
|
-
*/
|
|
67
|
-
function motorPara({ segundos = 0, pedido = '', alfa = false, fps = 30, entrega } = {}) {
|
|
68
|
-
const t = String(pedido).toLowerCase();
|
|
69
|
-
if (/remotion/.test(t)) return { motor: 'remotion', motivo: 'foi pedido pelo nome' };
|
|
70
|
-
if (/(est[uú]dio|studio|r[aá]pido|agora|j[aá])\b/.test(t) && segundos <= 60) {
|
|
71
|
-
return { motor: 'estudio', motivo: 'pedido rápido e curto' };
|
|
72
|
-
}
|
|
73
|
-
if (alfa || /alfa|transpar[eê]nci|prores|canal alfa/.test(t)) {
|
|
74
|
-
return { motor: 'remotion', motivo: 'canal alfa e ProRes só existem no Remotion' };
|
|
75
|
-
}
|
|
76
|
-
if (entrega === 'cinema' || fps > 30) {
|
|
77
|
-
return { motor: 'remotion', motivo: 'fps alto pede render determinístico' };
|
|
78
|
-
}
|
|
79
|
-
if (segundos > 90) {
|
|
80
|
-
return { motor: 'remotion', motivo: 'vídeo longo: a gravação do navegador engasga' };
|
|
81
|
-
}
|
|
82
|
-
return { motor: 'estudio', motivo: 'curto: sai em segundos, sem instalar nada' };
|
|
83
|
-
}
|
|
84
|
-
|
|
85
67
|
/**
|
|
86
68
|
* A voz. Tom e idioma decidem; o nome exato quem resolve é o estúdio, que
|
|
87
69
|
* sabe o que está instalado — aqui sai a INTENÇÃO, que é o que o diretor
|
|
@@ -115,7 +97,7 @@ function ritmoPara(segundos) {
|
|
|
115
97
|
|
|
116
98
|
// ── 3. o plano ───────────────────────────────────────────────────────────
|
|
117
99
|
|
|
118
|
-
const CAMPOS_CENA = ['titulo', 'narracao', 'duracao', '
|
|
100
|
+
const CAMPOS_CENA = ['titulo', 'narracao', 'duracao', 'aTela', 'fundo', 'transicao',
|
|
119
101
|
'elementos', 'midia', 'camera', 'chaves', 'notas'];
|
|
120
102
|
|
|
121
103
|
/**
|
|
@@ -132,16 +114,21 @@ function normalizarPlano(bruto, { segundos } = {}) {
|
|
|
132
114
|
const cenas = Array.isArray(p.cenas) ? p.cenas : [];
|
|
133
115
|
if (!cenas.length) defeitos.push('o plano veio sem cenas');
|
|
134
116
|
|
|
135
|
-
const TRANSICOES = ['corte', 'fade', 'deslizar', 'subir', 'zoom', 'limpar', 'persiana', 'flash'];
|
|
136
117
|
const limpas = cenas.map((c, i) => {
|
|
137
118
|
const cena = {};
|
|
138
119
|
for (const k of CAMPOS_CENA) if (c && c[k] !== undefined) cena[k] = c[k];
|
|
139
120
|
cena.duracao = Math.max(1.2, Math.min(15, Number(cena.duracao) || 3.5));
|
|
140
121
|
cena.narracao = typeof cena.narracao === 'string' ? cena.narracao.trim() : '';
|
|
141
122
|
if (!cena.narracao) defeitos.push(`cena ${i + 1} sem narração`);
|
|
142
|
-
if (!cena.titulo && !cena.elementos) defeitos.push(`cena ${i + 1} sem nada na tela`);
|
|
143
|
-
|
|
144
|
-
|
|
123
|
+
if (!cena.titulo && !cena.aTela && !cena.elementos) defeitos.push(`cena ${i + 1} sem nada na tela`);
|
|
124
|
+
/* A transição é texto: "o logo desce e o fundo abre com ele", não um
|
|
125
|
+
item de lista. O rodízio que havia aqui (fade, deslizar, subir…
|
|
126
|
+
em ordem) escondia o defeito em vez de apontá-lo: preenchia a
|
|
127
|
+
lacuna com a próxima palavra da fila, e o vídeo saía com oito
|
|
128
|
+
transições que ninguém escolheu. Cena sem transição é DEFEITO, e
|
|
129
|
+
volta para o diretor decidir. */
|
|
130
|
+
cena.transicao = typeof cena.transicao === 'string' ? cena.transicao.trim() : '';
|
|
131
|
+
if (!cena.transicao) defeitos.push(`cena ${i + 1} não diz como entra`);
|
|
145
132
|
cena.midia = Array.isArray(cena.midia) ? cena.midia.filter((m) => m && m.busca) : [];
|
|
146
133
|
return cena;
|
|
147
134
|
});
|
|
@@ -153,11 +140,13 @@ function normalizarPlano(bruto, { segundos } = {}) {
|
|
|
153
140
|
const fecha = !alvo || Math.abs(total - alvo) <= Math.max(3, alvo * 0.2);
|
|
154
141
|
if (!fecha) defeitos.push(`a soma das cenas dá ${total}s e o pedido é ${alvo}s`);
|
|
155
142
|
|
|
143
|
+
if (!String(p.visual || '').trim()) defeitos.push('o plano não diz a direção de arte (visual): paleta, ar e tipografia');
|
|
144
|
+
|
|
156
145
|
const plano = {
|
|
157
146
|
titulo: String(p.titulo || '').trim() || 'Sem título',
|
|
158
147
|
formato: p.formato === 'apresentacao' ? 'apresentacao' : 'video',
|
|
159
148
|
proporcao: ['16:9', '9:16', '1:1', '4:5'].includes(p.proporcao) ? p.proporcao : '16:9',
|
|
160
|
-
|
|
149
|
+
visual: String(p.visual || '').trim() || null,
|
|
161
150
|
tom: p.tom || 'neutro',
|
|
162
151
|
publico: p.publico || null,
|
|
163
152
|
promessa: p.promessa || null, // o que a pessoa leva do vídeo
|
|
@@ -172,8 +161,6 @@ function normalizarPlano(bruto, { segundos } = {}) {
|
|
|
172
161
|
midia: limpas.flatMap((c, i) => c.midia.map((m) => ({ cena: i + 1, ...m }))),
|
|
173
162
|
creditos: !!p.creditos,
|
|
174
163
|
};
|
|
175
|
-
plano.motor = motorPara({ segundos: total, pedido: p.pedido || '', alfa: !!p.alfa,
|
|
176
|
-
fps: Number(p.fps) || 30 });
|
|
177
164
|
return { plano, defeitos, ok: defeitos.length === 0 };
|
|
178
165
|
}
|
|
179
166
|
|
|
@@ -192,7 +179,7 @@ Devolva SÓ um bloco \`\`\`json com este formato, sem texto antes nem depois:
|
|
|
192
179
|
"titulo": "…",
|
|
193
180
|
"formato": "${formato}",
|
|
194
181
|
"proporcao": "16:9 | 9:16 | 1:1 | 4:5",
|
|
195
|
-
"
|
|
182
|
+
"visual": "a direção de arte em uma ou duas frases: paleta (diga as cores em hex), que ar tem (limpo, denso, luminoso, sujo), que tipografia pede. NÃO existe lista de temas — descreva o que este vídeo tem de próprio.",
|
|
196
183
|
"tom": "como deve soar (institucional, urgente, documental, leve…)",
|
|
197
184
|
"publico": "para quem é",
|
|
198
185
|
"promessa": "o que a pessoa leva ao terminar de ver",
|
|
@@ -202,9 +189,9 @@ Devolva SÓ um bloco \`\`\`json com este formato, sem texto antes nem depois:
|
|
|
202
189
|
"titulo": "o que aparece grande na tela",
|
|
203
190
|
"narracao": "a frase FALADA, escrita para ser lida em voz alta",
|
|
204
191
|
"duracao": 3.5,
|
|
205
|
-
"
|
|
206
|
-
"fundo": "
|
|
207
|
-
"transicao": "
|
|
192
|
+
"aTela": "o que se vê nesta cena, descrito como você descreveria para um motion designer: onde está cada coisa, que tamanho tem, o que domina o quadro",
|
|
193
|
+
"fundo": "o fundo desta cena — uma cor, um gradiente (diga as cores), uma imagem da lista midia, ou o que ele vira no meio da cena",
|
|
194
|
+
"transicao": "como esta cena ENTRA, descrito: o que se move, em que direção, quanto dura. Corte seco também é resposta — diga 'corte'.",
|
|
208
195
|
"midia": [ { "busca": "o que procurar", "tipo": "imagem | logo", "papel": "fundo | destaque | selo", "semFundo": false } ],
|
|
209
196
|
"camera": "aproximar | afastar | pan-direita | pan-esquerda | subir | descer | empurrar | balanco | nenhuma",
|
|
210
197
|
"chaves": "descreva em uma frase o movimento principal desta cena (o que entra, o que atravessa, o que cresce)",
|
|
@@ -219,6 +206,7 @@ REGRAS QUE SEPARAM UM VÍDEO BOM DE UM GENÉRICO:
|
|
|
219
206
|
3. ARCO. Primeira cena prende em 2 segundos, o miolo entrega, a última pede uma ação. Sem "Introdução / Desenvolvimento / Conclusão".
|
|
220
207
|
4. NÚMERO É CONCRETO. Nada de "muitos clientes": ou tem o número, ou a frase muda.
|
|
221
208
|
5. RITMO VARIADO. Cenas de durações diferentes e transições diferentes. Tudo em fade parece slide.
|
|
209
|
+
5b. NADA DE GAVETA. Não existe tema pronto, layout pronto nem fundo pronto: é você que decide a cor, a tipografia e a composição DESTE vídeo. Dois vídeos seus sobre assuntos diferentes não podem sair parecidos.
|
|
222
210
|
6. MÍDIA COM PROPÓSITO. Só peça imagem ou logo quando ela PROVA algo. Diga o termo de busca de verdade ("painel solar em telhado residencial", não "imagem legal").
|
|
223
211
|
7. MOVIMENTO. Em cada cena diga em "chaves" o movimento principal — é ele que tira a cara de apresentação parada.
|
|
224
212
|
8. CÂMERA COM PARCIMÔNIA. Movimento de quadro em UMA cena a cada duas ou três ("camera": "nenhuma" nas outras): o que dá ritmo é alternar cena parada e cena que se move. Em cena com foto de fundo, prefira "aproximar" ou "empurrar".`;
|
|
@@ -279,16 +267,18 @@ function pauta(plano) {
|
|
|
279
267
|
const p = plano || {};
|
|
280
268
|
const linhas = [];
|
|
281
269
|
linhas.push(`PLANO DE PRODUÇÃO — ${p.titulo}`);
|
|
282
|
-
linhas.push(`formato ${p.formato} · ${p.proporcao} · ${p.segundos}s ·
|
|
283
|
-
linhas.push(`
|
|
270
|
+
linhas.push(`formato ${p.formato} · ${p.proporcao} · ${p.segundos}s · tom ${p.tom}`);
|
|
271
|
+
if (p.visual) linhas.push(`visual: ${p.visual}`);
|
|
284
272
|
if (p.promessa) linhas.push(`promessa: ${p.promessa}`);
|
|
285
273
|
if (p.publico) linhas.push(`público: ${p.publico}`);
|
|
286
274
|
linhas.push(`voz: ${p.voz && p.voz.perfil} · ${p.voz && p.voz.instrucao} · ${p.voz && p.voz.idioma}`);
|
|
287
275
|
if (p.trilha) linhas.push(`trilha: ${p.trilha.busca || p.trilha.arquivo} (volume ${p.trilha.volume})`);
|
|
288
276
|
linhas.push('');
|
|
289
277
|
(p.cenas || []).forEach((c, i) => {
|
|
290
|
-
linhas.push(`CENA ${i + 1} — ${c.duracao}s ·
|
|
278
|
+
linhas.push(`CENA ${i + 1} — ${c.duracao}s · entra: ${c.transicao}`);
|
|
291
279
|
if (c.titulo) linhas.push(` na tela: ${c.titulo}`);
|
|
280
|
+
if (c.aTela) linhas.push(` composição: ${c.aTela}`);
|
|
281
|
+
if (c.fundo) linhas.push(` fundo: ${typeof c.fundo === 'string' ? c.fundo : JSON.stringify(c.fundo)}`);
|
|
292
282
|
linhas.push(` narração: ${c.narracao}`);
|
|
293
283
|
if (c.camera && c.camera !== 'nenhuma') linhas.push(` câmera: ${c.camera}`);
|
|
294
284
|
if (c.chaves) linhas.push(` movimento: ${c.chaves}`);
|
|
@@ -303,7 +293,7 @@ function pauta(plano) {
|
|
|
303
293
|
}
|
|
304
294
|
|
|
305
295
|
module.exports = {
|
|
306
|
-
precisaDeProducao,
|
|
296
|
+
precisaDeProducao, vozPara, ritmoPara,
|
|
307
297
|
normalizarPlano, extrairJSON, planejar, pauta,
|
|
308
298
|
DIRETOR, REVISAO,
|
|
309
299
|
};
|