primocode 10.0.0 → 10.0.9
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 +2 -2
- package/bin/primocode.js +69 -1
- package/lib/act.js +118 -19
- package/lib/browser.js +89 -5
- package/lib/catalogo.js +1 -1
- package/lib/claude-engine.js +38 -17
- package/lib/codex-engine.js +27 -9
- package/lib/entrada.js +73 -9
- package/lib/estudio/cinema.js +27 -9
- package/lib/estudio/diretor.js +13 -1
- package/lib/estudio/ende.js +16 -5
- package/lib/estudio/estilo.js +23 -8
- package/lib/estudio/gerativo.js +1453 -0
- package/lib/estudio/hibrido.js +165 -0
- package/lib/estudio/index.js +244 -36
- package/lib/estudio/kit/gerativo.jsx +980 -0
- package/lib/estudio/kit/index.jsx +3 -1
- package/lib/estudio/kit/tres.jsx +308 -0
- package/lib/estudio/kit.js +7 -1
- package/lib/estudio/lancamento.js +28 -0
- package/lib/estudio/referencias.js +105 -0
- package/lib/estudio/remotion.js +53 -9
- package/lib/estudio/ritmo.js +2 -2
- package/lib/estudio/sons-leves.js +38 -1
- package/lib/estudio/variedade.js +105 -0
- package/lib/estudio/vox.js +37 -8
- package/lib/mcp.js +156 -0
- package/lib/personas.js +67 -0
- package/lib/skills.js +36 -0
- package/lib/tools.js +22 -3
- package/lib/ui.js +41 -6
- package/lib/video/auto.js +329 -0
- package/lib/video/camadas.js +207 -0
- package/lib/video/cli.js +1 -1
- package/lib/video/filters.js +39 -6
- package/lib/video/fx3d/Camadas.jsx +64 -0
- package/lib/video/fx3d/Objetos.jsx +128 -0
- package/lib/video/fx3d/Root.jsx +18 -0
- package/lib/video/fx3d/modelos.jsx +304 -0
- package/lib/video/fx3d.js +108 -0
- package/lib/video/gerados.js +119 -0
- package/lib/video/objetos.js +111 -0
- package/lib/video/ops-fx.js +453 -0
- package/lib/video/ops.js +30 -5
- package/lib/video/paralelo.js +50 -0
- package/lib/video/pedido.js +191 -0
- package/lib/video/rastrear.js +95 -0
- package/lib/video/rastro.js +276 -0
- package/lib/video/render.js +67 -14
- package/lib/video/texto.js +6 -4
- package/lib/video/timeline.js +9 -1
- package/package.json +2 -2
- package/skills/motion-de-primeira/SKILL.md +5 -4
- package/studio/narrar.py +10 -7
- package/studio/rastrear.py +399 -0
package/lib/video/render.js
CHANGED
|
@@ -82,6 +82,8 @@ async function temPillow() {
|
|
|
82
82
|
|
|
83
83
|
/** As camadas de texto da janela: {índiceDaFaixa: arquivo .ffconcat}. */
|
|
84
84
|
async function camadasDeTexto(pasta, tl, { from, to, escala }) {
|
|
85
|
+
const avisosPos = [];
|
|
86
|
+
const posicao = require('./gerados.js').posicoesDeLegenda(pasta, tl, avisosPos);
|
|
85
87
|
const faixas = tl.tracks.map((t, i) => ({ t, i })).filter(({ t }) => t.type === 'text' && t.clips.length && !t.hidden);
|
|
86
88
|
if (!faixas.length) return { camadas: {}, avisos: [] };
|
|
87
89
|
if (!(await temPillow())) {
|
|
@@ -96,7 +98,7 @@ async function camadasDeTexto(pasta, tl, { from, to, escala }) {
|
|
|
96
98
|
const estados = [];
|
|
97
99
|
const camadas = {};
|
|
98
100
|
for (const { t, i } of faixas) {
|
|
99
|
-
const { segmentos, desenhos } = TX.estadosDaFaixa(tl, t, { from, to, escala });
|
|
101
|
+
const { segmentos, desenhos } = TX.estadosDaFaixa(tl, t, { from, to, escala, posicao });
|
|
100
102
|
if (!Object.keys(desenhos).length) continue;
|
|
101
103
|
for (const [chave, items] of Object.entries(desenhos)) {
|
|
102
104
|
if (!fs.existsSync(arquivoDe(chave)) && !estados.some((e) => e.chave === chave)) estados.push({ chave, file: arquivoDe(chave), items });
|
|
@@ -112,13 +114,13 @@ async function camadasDeTexto(pasta, tl, { from, to, escala }) {
|
|
|
112
114
|
try { fs.unlinkSync(job); } catch {}
|
|
113
115
|
if (!r.ok) return { camadas: {}, avisos: [`os textos não foram desenhados: ${r.error}`] };
|
|
114
116
|
}
|
|
115
|
-
return { camadas, avisos: [], estados: estados.length };
|
|
117
|
+
return { camadas, avisos: [...new Set(avisosPos)], estados: estados.length };
|
|
116
118
|
}
|
|
117
119
|
|
|
118
120
|
// ── rodar o ffmpeg ───────────────────────────────────────────────────────
|
|
119
121
|
|
|
120
|
-
async function rodarGrafo(pasta, g, saida) {
|
|
121
|
-
const arq = path.join(pasta, 'cache', `grafo-${process.pid}.txt`);
|
|
122
|
+
async function rodarGrafo(pasta, g, saida, sufixo = '') {
|
|
123
|
+
const arq = path.join(pasta, 'cache', `grafo-${process.pid}${sufixo}.txt`);
|
|
122
124
|
fs.mkdirSync(path.dirname(arq), { recursive: true });
|
|
123
125
|
fs.writeFileSync(arq, g.grafo);
|
|
124
126
|
// O grafo vai por arquivo: com cinquenta clipes ele passa do teto de
|
|
@@ -130,29 +132,80 @@ async function rodarGrafo(pasta, g, saida) {
|
|
|
130
132
|
return { ...r, grafo: arq };
|
|
131
133
|
}
|
|
132
134
|
|
|
133
|
-
|
|
135
|
+
/* O EXPORT EM PARTES: o grafo do ffmpeg é quase todo de um núcleo só (cada
|
|
136
|
+
filtro roda numa thread), e um vídeo com pessoa recortada, fundo novo,
|
|
137
|
+
3D e legenda passa a maior parte do tempo nele. Então o VÍDEO sai em
|
|
138
|
+
pedaços de quadros, cada um num ffmpeg, ao mesmo tempo, e junta por
|
|
139
|
+
concat sem recodificar; o ÁUDIO sai inteiro, numa passada só (é barato,
|
|
140
|
+
e cortar AAC em pedaços deixa estalo na emenda). As emendas caem em
|
|
141
|
+
quadro inteiro: o quadro N sai igual com 1 ou com 9 pedaços. */
|
|
142
|
+
async function exportar(pasta, tl, { out, from = 0, to, preview = false, crf = 20, partes } = {}) {
|
|
134
143
|
const c = conferir();
|
|
135
144
|
if (!c.ok) return c;
|
|
145
|
+
const ger = await require('./gerados.js').garantir(pasta, tl);
|
|
146
|
+
if (!ger.ok) return ger;
|
|
136
147
|
const escala = preview ? 0.5 : 1;
|
|
137
148
|
const ate = to === undefined ? T.duracao(tl) : Number(to);
|
|
138
|
-
const tx = await camadasDeTexto(pasta, tl, { from, to: ate, escala });
|
|
139
|
-
const g = F.montarGrafo(tl, { from, to: ate, camadasDeTexto: tx.camadas, escala });
|
|
140
149
|
const destino = path.resolve(out || path.join(pasta, 'saida', `${tl.nome || 'video'}${preview ? '-preview' : ''}.mp4`));
|
|
141
150
|
fs.mkdirSync(path.dirname(destino), { recursive: true });
|
|
142
151
|
const inicio = Date.now();
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
152
|
+
const PAR = require('./paralelo.js');
|
|
153
|
+
const total = ate - from;
|
|
154
|
+
const n = partes !== undefined ? Math.max(1, Number(partes)) : Math.max(1, Math.min(PAR.quantos(), Math.floor(total / (tl.fps * 2))));
|
|
155
|
+
const codec = argsDoCodec(codecDeVideo(), crf);
|
|
156
|
+
const extra = ger.feitos ? { gerados: ger.feitos, geradosS: ger.tookS } : {};
|
|
157
|
+
if (n === 1) {
|
|
158
|
+
const tx = await camadasDeTexto(pasta, tl, { from, to: ate, escala });
|
|
159
|
+
const g = F.montarGrafo(tl, { from, to: ate, camadasDeTexto: tx.camadas, escala });
|
|
160
|
+
const r = await rodarGrafo(pasta, g, [
|
|
161
|
+
'-map', g.saidaV, '-map', g.saidaA, '-t', F.num(g.segundos), '-r', String(tl.fps),
|
|
162
|
+
...codec, '-pix_fmt', 'yuv420p', '-c:a', 'aac', '-b:a', '192k', '-movflags', '+faststart', destino,
|
|
163
|
+
]);
|
|
164
|
+
if (!r.ok) return { ok: false, error: 'o ffmpeg falhou', detalhe: r.erro.slice(-600), grafo: r.grafo };
|
|
165
|
+
return { ok: true, file: destino, s: T.r2(g.segundos), size: `${g.W}x${g.H}`, mb: T.r2(fs.statSync(destino).size / 1048576),
|
|
166
|
+
tookS: Math.round((Date.now() - inicio) / 100) / 10, partes: 1, ...extra, ...(tx.avisos.length ? { avisos: tx.avisos } : {}) };
|
|
167
|
+
}
|
|
168
|
+
const tmp = path.join(pasta, 'cache', `export-${process.pid}`);
|
|
169
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
170
|
+
fs.mkdirSync(tmp, { recursive: true });
|
|
171
|
+
const cortes = Array.from({ length: n + 1 }, (_, i) => from + Math.round((total * i) / n));
|
|
172
|
+
// Os PNGs do texto antes, em fila: dois pedaços desenhando o mesmo estado ao mesmo tempo se atropelariam.
|
|
173
|
+
const avisos = new Set();
|
|
174
|
+
const pedacos = [];
|
|
175
|
+
for (let i = 0; i < n; i++) {
|
|
176
|
+
const tx = await camadasDeTexto(pasta, tl, { from: cortes[i], to: cortes[i + 1], escala });
|
|
177
|
+
tx.avisos.forEach((a) => avisos.add(a));
|
|
178
|
+
pedacos.push({ de: cortes[i], ate: cortes[i + 1], tx, arq: path.join(tmp, `v${String(i).padStart(3, '0')}.mp4`) });
|
|
179
|
+
}
|
|
180
|
+
const res = await PAR.emParalelo([
|
|
181
|
+
...pedacos.map((p, i) => () => {
|
|
182
|
+
const g = F.montarGrafo(tl, { from: p.de, to: p.ate, camadasDeTexto: p.tx.camadas, escala, audio: false });
|
|
183
|
+
return rodarGrafo(pasta, g, ['-map', g.saidaV, '-frames:v', String(p.ate - p.de), '-r', String(tl.fps), ...codec, '-pix_fmt', 'yuv420p', p.arq], `-v${i}`);
|
|
184
|
+
}),
|
|
185
|
+
() => {
|
|
186
|
+
const g = F.montarGrafo(tl, { from, to: ate, escala, video: false });
|
|
187
|
+
return rodarGrafo(pasta, g, ['-map', g.saidaA, '-t', F.num(g.segundos), '-c:a', 'aac', '-b:a', '192k', path.join(tmp, 'audio.m4a')], '-a');
|
|
188
|
+
},
|
|
189
|
+
], n + 1);
|
|
190
|
+
const ruim = res.find((r) => !r.ok);
|
|
191
|
+
if (ruim) return { ok: false, error: 'o ffmpeg falhou', detalhe: ruim.erro.slice(-600), grafo: ruim.grafo };
|
|
192
|
+
const lista = path.join(tmp, 'lista.txt');
|
|
193
|
+
fs.writeFileSync(lista, pedacos.map((p) => `file '${p.arq.replace(/'/g, "'\\''")}'`).join('\n') + '\n');
|
|
194
|
+
const j = await video.rodar(video.ffmpeg(), ['-hide_banner', '-loglevel', 'error', '-y', '-f', 'concat', '-safe', '0', '-i', lista,
|
|
195
|
+
'-i', path.join(tmp, 'audio.m4a'), '-map', '0:v', '-map', '1:a', '-c', 'copy', '-movflags', '+faststart', destino]);
|
|
196
|
+
if (!j.ok) return { ok: false, error: 'não juntei os pedaços do export', detalhe: j.erro.slice(-400) };
|
|
197
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
198
|
+
return { ok: true, file: destino, s: T.r2(total / tl.fps), size: `${T.par(tl.width * escala)}x${T.par(tl.height * escala)}`,
|
|
199
|
+
mb: T.r2(fs.statSync(destino).size / 1048576), tookS: Math.round((Date.now() - inicio) / 100) / 10, partes: n, ...extra,
|
|
200
|
+
...(avisos.size ? { avisos: [...avisos] } : {}) };
|
|
150
201
|
}
|
|
151
202
|
|
|
152
203
|
/** Os quadros pedidos, com a grade 0–1 e o número do quadro, numa folha só. */
|
|
153
204
|
async function inspecionar(pasta, tl, { frames, escala = 0.5, out } = {}) {
|
|
154
205
|
const c = conferir();
|
|
155
206
|
if (!c.ok) return c;
|
|
207
|
+
const ger = await require('./gerados.js').garantir(pasta, tl);
|
|
208
|
+
if (!ger.ok) return ger;
|
|
156
209
|
const dur = T.duracao(tl);
|
|
157
210
|
const lista = [...new Set(frames.map((f) => Math.max(0, Math.min(dur - 1, Math.round(Number(f))))))].slice(0, 12);
|
|
158
211
|
const dir = path.join(pasta, 'cache', 'inspect');
|
package/lib/video/texto.js
CHANGED
|
@@ -36,7 +36,7 @@ function palavrasDo(c) {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/** O estado de UM clipe de texto no quadro local `lf` — ou null (não aparece). */
|
|
39
|
-
function estado(tl, c, lf) {
|
|
39
|
+
function estado(tl, c, lf, pos = null) {
|
|
40
40
|
const st = { ...ESTILO_PADRAO, ...(c.text.style || {}) };
|
|
41
41
|
const d = c.durationFrames;
|
|
42
42
|
const anim = c.text.animation || 'none';
|
|
@@ -85,7 +85,7 @@ function estado(tl, c, lf) {
|
|
|
85
85
|
const W = tl.width;
|
|
86
86
|
const H = tl.height;
|
|
87
87
|
const item = {
|
|
88
|
-
cx: r3(c.transform.cx * W), cy: r3(c.transform.cy * H), w: Math.round(c.transform.w * W),
|
|
88
|
+
cx: r3((pos ? pos.cx : c.transform.cx) * W), cy: r3((pos ? pos.cy : c.transform.cy) * H), w: Math.round(c.transform.w * W),
|
|
89
89
|
size: Math.round(st.size * H), font: st.font, color: st.color, align: st.align,
|
|
90
90
|
stroke: st.stroke, sw: Math.round(st.strokeWidth * st.size * H), bg: st.bg,
|
|
91
91
|
scale: r3(scale), alpha: Math.round(alpha * 100) / 100, words,
|
|
@@ -97,8 +97,10 @@ function estado(tl, c, lf) {
|
|
|
97
97
|
/**
|
|
98
98
|
* Os estados de uma faixa na janela [from, to): segmentos consecutivos com o
|
|
99
99
|
* mesmo desenho viram um só. Devolve {segmentos:[{dur, chave}], desenhos}.
|
|
100
|
+
* `posicao(clipe, quadro)` → {cx, cy} | null: a legenda que SEGUE alguém
|
|
101
|
+
* (a cabeça) troca o lugar do transform pelo que ela devolver, quadro a quadro.
|
|
100
102
|
*/
|
|
101
|
-
function estadosDaFaixa(tl, faixa, { from = 0, to, escala = 1 } = {}) {
|
|
103
|
+
function estadosDaFaixa(tl, faixa, { from = 0, to, escala = 1, posicao = null } = {}) {
|
|
102
104
|
const ate = to === undefined ? T.duracao(tl) : to;
|
|
103
105
|
const clips = faixa.clips.filter((c) => !c.hidden && T.fim(c) > from && c.startFrame < ate);
|
|
104
106
|
const escalar = (it) => (escala === 1 ? it : {
|
|
@@ -114,7 +116,7 @@ function estadosDaFaixa(tl, faixa, { from = 0, to, escala = 1 } = {}) {
|
|
|
114
116
|
for (let j = k; j < clips.length && clips[j].startFrame <= f; j++) {
|
|
115
117
|
const c = clips[j];
|
|
116
118
|
if (f >= T.fim(c)) continue;
|
|
117
|
-
const it = estado(tl, c, f - c.startFrame);
|
|
119
|
+
const it = estado(tl, c, f - c.startFrame, posicao ? posicao(c, f) : null);
|
|
118
120
|
if (it) itens.push(escalar(it));
|
|
119
121
|
}
|
|
120
122
|
const json = JSON.stringify(itens);
|
package/lib/video/timeline.js
CHANGED
|
@@ -267,6 +267,7 @@ function compacto(tl, c, track) {
|
|
|
267
267
|
if (Object.keys(tf).length) o.xf = tf;
|
|
268
268
|
if (Object.values(c.crop).some((v) => v)) o.crop = c.crop;
|
|
269
269
|
if (c.fit !== 'cover') o.fit = c.fit;
|
|
270
|
+
if (c.papel) o.role = c.papel;
|
|
270
271
|
const kf = Object.keys(c.keyframes || {}).filter((k) => c.keyframes[k].length);
|
|
271
272
|
if (kf.length) o.kf = kf;
|
|
272
273
|
if (c.effects.length) o.fx = c.effects.map((e) => e.type);
|
|
@@ -289,15 +290,22 @@ function vista(tl, { from = 0, to = Infinity } = {}) {
|
|
|
289
290
|
});
|
|
290
291
|
const f = { i, type: t.type, n: t.clips.length, clips };
|
|
291
292
|
if (t.muted) f.muted = true;
|
|
293
|
+
if (t.role) f.role = t.role;
|
|
292
294
|
return f;
|
|
293
295
|
});
|
|
294
296
|
const assets = {};
|
|
295
297
|
for (const a of Object.values(tl.assets)) {
|
|
296
298
|
assets[a.id] = { nome: a.nome, kind: a.kind, s: r2(a.seconds || 0) };
|
|
297
299
|
if (a.width) assets[a.id].size = `${a.width}x${a.height}`;
|
|
298
|
-
if (!a.hasAudio && a.kind === 'video') assets[a.id].mudo = true;
|
|
300
|
+
if (!a.hasAudio && a.kind === 'video' && !a.gerado) assets[a.id].mudo = true;
|
|
301
|
+
if (a.gerado) assets[a.id].gerado = a.gerado.tipo;
|
|
299
302
|
}
|
|
300
303
|
const out = { fps: tl.fps, size: `${tl.width}x${tl.height}`, dur: duracao(tl), durS: r2(segundos(tl, duracao(tl))), assets, tracks };
|
|
304
|
+
if (tl.fx) {
|
|
305
|
+
// As receitas dos efeitos, em segundos da ORIGEM (o que o rastro mediu).
|
|
306
|
+
out.fx = {};
|
|
307
|
+
for (const [k, v] of Object.entries(tl.fx)) out.fx[k] = { objetos: (v.objetos || []).map((o) => `${o.id}:${o.modelo}@${o.mao} ${o.de}-${o.ate}s`), ...(v.fusao ? { fusao: v.fusao } : {}) };
|
|
308
|
+
}
|
|
301
309
|
if (tl.markers.length) out.markers = tl.markers.filter((m) => m.frame >= from && m.frame < to).slice(0, 60);
|
|
302
310
|
return out;
|
|
303
311
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primocode",
|
|
3
|
-
"version": "10.0.
|
|
3
|
+
"version": "10.0.9",
|
|
4
4
|
"description": "PrimoCode — agente de engenharia com IA e cursor próprio. Requer conta Conecta Primo AI (Premium ou Super). Cria arquivos, roda comandos, controla navegador e desktop: abre apps, clica em botões e ícones pelo nome, digita e usa atalhos.",
|
|
5
5
|
"main": "bin/primocode.js",
|
|
6
6
|
"bin": {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"start": "node bin/primocode.js",
|
|
11
|
-
"test": "node test/tools.test.js && node test/ui.test.js && node test/entrada.test.js && node test/transparencia.test.js && node test/permissao.test.js && node test/compatibilidade.test.js && node test/skills.test.js && node test/skills-de-fora.test.js && node test/skills-novas.test.js && node test/comandos.test.js && node test/estudio.test.js && node test/voz.test.js && node test/modo.test.js && node test/pasta.test.js && node test/projeto.test.js && node test/nome-projeto.test.js && node test/api-propria.test.js && node test/claude-engine.test.js && node test/codex.test.js && node test/conferir.test.js && node test/catalogo.test.js && node test/desktop.test.js && node test/seta.test.js && node test/effort.test.js && node test/regressao.test.js && node test/memoria-conversa.test.js && node test/repeticao.test.js && node test/casar.test.js && node test/recado.test.js && node test/releitura.test.js && node test/snapshot.test.js && node test/video.test.js && node test/limpeza.test.js && node test/autonomia.test.js && node test/desfazer.test.js && node test/desfazer-git.test.js && node test/referencia.test.js && node test/parar.test.js && node test/continuar.test.js && node test/pipeline.test.js && node test/conta.test.mjs && node test/nuvem.test.js && node test/prazo.test.js && node test/primeira-vez.test.js && node test/fala.test.js && node test/especialistas.test.js && node test/empurrao.test.js && node test/terminal-novo.test.js && node test/markdown.test.js && node test/coerencia.test.js && node test/contexto.test.js && node test/perigo.test.js && node test/rapidez.test.js && node test/mascotes.test.js && node test/comandos-extra.test.js && node test/offline.test.js && node test/edicao.test.js && node test/composicao.test.js && node test/acervo-site.test.js && node test/impacto.test.js && node test/cinema.test.js && node test/producao.test.js && node test/ende.test.js && node test/capa.test.js && node test/broll.test.js && node test/morfar.test.js && node test/motion.test.js && node test/motion-roteiro.test.js && node test/kit-motion.test.js"
|
|
11
|
+
"test": "node test/tools.test.js && node test/ui.test.js && node test/entrada.test.js && node test/transparencia.test.js && node test/permissao.test.js && node test/compatibilidade.test.js && node test/skills.test.js && node test/skills-de-fora.test.js && node test/skills-novas.test.js && node test/comandos.test.js && node test/estudio.test.js && node test/voz.test.js && node test/modo.test.js && node test/pasta.test.js && node test/projeto.test.js && node test/nome-projeto.test.js && node test/api-propria.test.js && node test/claude-engine.test.js && node test/codex.test.js && node test/conferir.test.js && node test/catalogo.test.js && node test/desktop.test.js && node test/seta.test.js && node test/effort.test.js && node test/regressao.test.js && node test/memoria-conversa.test.js && node test/repeticao.test.js && node test/casar.test.js && node test/recado.test.js && node test/releitura.test.js && node test/snapshot.test.js && node test/video.test.js && node test/limpeza.test.js && node test/autonomia.test.js && node test/desfazer.test.js && node test/desfazer-git.test.js && node test/referencia.test.js && node test/parar.test.js && node test/continuar.test.js && node test/pipeline.test.js && node test/conta.test.mjs && node test/nuvem.test.js && node test/prazo.test.js && node test/primeira-vez.test.js && node test/fala.test.js && node test/especialistas.test.js && node test/empurrao.test.js && node test/terminal-novo.test.js && node test/markdown.test.js && node test/coerencia.test.js && node test/contexto.test.js && node test/perigo.test.js && node test/rapidez.test.js && node test/mascotes.test.js && node test/comandos-extra.test.js && node test/offline.test.js && node test/edicao.test.js && node test/composicao.test.js && node test/acervo-site.test.js && node test/impacto.test.js && node test/cinema.test.js && node test/producao.test.js && node test/ende.test.js && node test/capa.test.js && node test/broll.test.js && node test/morfar.test.js && node test/motion.test.js && node test/motion-roteiro.test.js && node test/kit-motion.test.js && node test/gerativo.test.js && node test/hibrido.test.js && node test/mcp.test.js && node test/personas.test.js && node test/efeitos.test.js && node test/auto.test.js && node test/colar.test.js && node test/orquestra.test.js"
|
|
12
12
|
},
|
|
13
13
|
"engines": {
|
|
14
14
|
"node": ">=18.17.0"
|
|
@@ -54,13 +54,14 @@ Em vídeo de produto, anúncio e vinheta, escreva **uma cena de 8 a 15 s** no
|
|
|
54
54
|
- **`forma: "traco"`**: forma livre por `caminho` (`d` de SVG, caixa 0 0 100
|
|
55
55
|
100, M/L/C/Q/Z). `caminho` e `cor` animam:
|
|
56
56
|
`"caminho": [{"t":1.6,"v":BOLA},{"t":2.5,"v":FOGUETE,"e":"elastico"}]`.
|
|
57
|
-
- **`tipo: "grupo"`**: elementos que andam juntos, ou a tela dentro da tela
|
|
58
|
-
(`"recorte": true`). Filhos contam tempo e posição a partir do grupo.
|
|
59
57
|
- **A troca é no mesmo lugar e instante**: o foguete some onde o ícone nasce; o
|
|
60
58
|
app nasce do tamanho e da COR do ícone.
|
|
61
59
|
|
|
62
|
-
Por roteiro, o
|
|
63
|
-
`marca`, `lancamento
|
|
60
|
+
Por roteiro, o padrão é `hibrido` (dê `busca` em inglês por parte); `gerativo` (o motion), `vox` (explicar; dê
|
|
61
|
+
`destaque` e `fonte`), `marca`, `lancamento` pelo nome; `visual` troca a peça de uma parte.
|
|
62
|
+
|
|
63
|
+
Pedido de motion (ou sem referências): `estilo: "gerativo"`, só o roteiro
|
|
64
|
+
(`fala`, `destaque`); `roteiro.semente` sorteia outro, `modelo3d` traz o 3D.
|
|
64
65
|
|
|
65
66
|
## Cor e tipo
|
|
66
67
|
|
package/studio/narrar.py
CHANGED
|
@@ -256,13 +256,16 @@ def comando_falar(args):
|
|
|
256
256
|
# tempo de CADA PALAVRA volta junto (WordBoundary do edge-tts): é o que
|
|
257
257
|
# sincroniza a tipografia com a voz.
|
|
258
258
|
REGISTRO = {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
"
|
|
263
|
-
"
|
|
264
|
-
"
|
|
265
|
-
"
|
|
259
|
+
# 10.0: "os vídeos estão muito lentos" (29/09). Ritmo de anúncio por
|
|
260
|
+
# padrão — ninguém fala devagar num reels — e o respiro entre as partes
|
|
261
|
+
# caiu para o de uma vírgula: o corte seco é que dá energia.
|
|
262
|
+
"gancho": {"ritmo": "+18%", "tom": "+4Hz", "respiro": 0.12},
|
|
263
|
+
"numero": {"ritmo": "+8%", "tom": "+1Hz", "respiro": 0.18},
|
|
264
|
+
"contraste": {"ritmo": "+14%", "tom": "+2Hz", "respiro": 0.15},
|
|
265
|
+
"lista": {"ritmo": "+14%", "tom": "+2Hz", "respiro": 0.12},
|
|
266
|
+
"citacao": {"ritmo": "+6%", "tom": "+0Hz", "respiro": 0.2},
|
|
267
|
+
"cta": {"ritmo": "+16%", "tom": "+4Hz", "respiro": 0.1},
|
|
268
|
+
"normal": {"ritmo": "+14%", "tom": "+2Hz", "respiro": 0.12},
|
|
266
269
|
}
|
|
267
270
|
|
|
268
271
|
|