morfeo 0.6.1 → 0.6.2
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/index.d.ts
CHANGED
|
@@ -87,7 +87,7 @@ export type Capa = CapaBase &
|
|
|
87
87
|
| {tipo: 'fuente'; texto: string; etiqueta?: string; tam?: number; color?: string; colorTexto?: string}
|
|
88
88
|
| {tipo: 'seccion'; texto: string; tam?: number; color?: string}
|
|
89
89
|
| {tipo: 'insignia'; texto: string | number; tam?: number; fondo?: string; colorTexto?: string; inclinar?: number}
|
|
90
|
-
| {tipo: 'sello'; texto: string; tam?: number; color?: string; inclinar?: number}
|
|
90
|
+
| {tipo: 'sello'; texto: string; tam?: number; color?: string; inclinar?: number; /** caja detrás (sobre fondos claros) */ fondo?: string}
|
|
91
91
|
| {tipo: 'terminal'; lineas: (string | {texto: string; en?: Momento; salida?: boolean; color?: string})[]; titulo?: string; ancho?: number; tam?: number; fondo?: string; velocidad?: number}
|
|
92
92
|
| {tipo: 'nombre'; texto: string; sub?: string; tam?: number; fondo?: string; colorTexto?: string; borde?: string}
|
|
93
93
|
| {tipo: 'accion'; texto: string; tam?: number; color?: string; fondo?: string; colorTexto?: string}
|
package/lib/calidad.mjs
CHANGED
|
@@ -149,7 +149,9 @@ export async function revisarCuadros(mp4, d) {
|
|
|
149
149
|
// medido: escenas sin nada sobre fondo oscuro → media 13–15 y la cubeta más oscura con 97 % del cuadro;
|
|
150
150
|
// tele o pared pegadas a la cámara → desviación 12–16 con media > 130; los videos buenos del canal: desviación ≥ 27
|
|
151
151
|
negro: (s) => (s.media < 16 && s.desv < 9) || (s.media < 25 && s.dom > 0.9),
|
|
152
|
-
|
|
152
|
+
// (el segundo caso pide además un color dominante: una escena de día con cielo pastel también tiene desviación
|
|
153
|
+
// baja, ~17, pero su color más repetido no pasa del 25 %)
|
|
154
|
+
tapado: (s) => (s.dom > 0.58 && s.luzDom > 45) || (s.desv < 18 && s.media > 60 && s.dom > 0.4),
|
|
153
155
|
plano: (s) => s.desv < 14 && s.media >= 25 && s.media <= 60,
|
|
154
156
|
};
|
|
155
157
|
const TEXTO = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "morfeo",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "Genera videos verticales animados (Remotion + Three.js) con voz, música y subtítulos, desde un guion en código o escrito por IA.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -686,7 +686,8 @@ const AMBIENTES: Record<NombreAmbiente, Def> = {
|
|
|
686
686
|
espalda: {pos: [2.3, 1.9, 1.3], mirar: [-0.2, 1.35, -3.4], fov: 46, a: {pos: [2.15, 1.85, 1.0]}},
|
|
687
687
|
},
|
|
688
688
|
orden: ['general', 'persona', 'maquina', 'espalda', 'pasillo'],
|
|
689
|
-
|
|
689
|
+
// general y espalda dejan a la persona al centro, justo donde caen las capas 2D
|
|
690
|
+
ordenFondo: ['pasillo', 'maquina'],
|
|
690
691
|
anclas: {maquina: [0.6, 1.42, -3.0], persona: [0.6, 1.6, -0.9], neon: [2.6, 2.9, -3.88], pasillo: [-4.4, 1.4, -0.6]},
|
|
691
692
|
persona: {pos: [0.6, 0, -0.95], rot: [0, 180, 0], pose: 'parado'},
|
|
692
693
|
Set: SetArcade,
|
package/remotion/motor/Video.tsx
CHANGED
|
@@ -413,7 +413,8 @@ const MarcaEsquina: React.FC<{tema: Tema}> = ({tema}) => {
|
|
|
413
413
|
};
|
|
414
414
|
|
|
415
415
|
export const VideoMotor: React.FC<{d: Datos; T: number; estilos?: Record<string, any>; miniatura?: boolean; sinMarca?: boolean}> = ({d, T, estilos, miniatura, sinMarca}) => {
|
|
416
|
-
const
|
|
416
|
+
const conAmbiente = !!d.mundo?.some((c: any) => c.tipo === 'ambiente');
|
|
417
|
+
const tema = temaDe(d.estilo, d.tema, estilos, conAmbiente);
|
|
417
418
|
const n = d.escenas.length;
|
|
418
419
|
let si = d.escenas.findIndex((e) => T < e.t0 + e.dur);
|
|
419
420
|
if (si < 0) si = n - 1;
|
|
@@ -473,6 +474,8 @@ export const VideoMotor: React.FC<{d: Datos; T: number; estilos?: Record<string,
|
|
|
473
474
|
<Mundo d={d} si={si} t={t} T={T} tema={tema} />
|
|
474
475
|
</AbsoluteFill>
|
|
475
476
|
) : null}
|
|
477
|
+
{/* un ambiente puede ser claro (oficina): sombra arriba, donde van subtítulos y titular, para que el texto blanco se lea (en el cierre, todo el cuadro) */}
|
|
478
|
+
{conAmbiente ? <AbsoluteFill style={{background: e.tipo === 'cta' ? 'rgba(0,0,0,0.42)' : 'linear-gradient(180deg, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0.38) 30%, rgba(0,0,0,0) 46%)'}} /> : null}
|
|
476
479
|
{enTransicion && prev ? (
|
|
477
480
|
<AbsoluteFill style={estiloPrevio}>
|
|
478
481
|
<EscenaVista e={prev} t={prev.dur + t} tema={tema} semilla={semilla} sinFondo={conMundo} />
|
|
@@ -204,6 +204,7 @@ export const Anotacion: React.FC<{c: Capa; ctx: CtxEscena}> = ({c, ctx}) => {
|
|
|
204
204
|
border: `${Math.max(4, tam * 0.1)}px solid ${color}`,
|
|
205
205
|
outline: `${Math.max(2, tam * 0.04)}px solid ${color}`,
|
|
206
206
|
outlineOffset: tam * 0.1,
|
|
207
|
+
background: c.fondo,
|
|
207
208
|
borderRadius: tam * 0.2,
|
|
208
209
|
padding: `${tam * 0.12}px ${tam * 0.42}px`,
|
|
209
210
|
fontFamily: T.fuentes.titulo,
|
|
@@ -11,6 +11,22 @@
|
|
|
11
11
|
import type {Marca} from '../tipos';
|
|
12
12
|
import type {Capa} from './capas';
|
|
13
13
|
import type {Tema} from './tema';
|
|
14
|
+
import {ancho} from '../estilo';
|
|
15
|
+
|
|
16
|
+
/** Renglones que ocupa un texto al partirse por palabras en `maxAncho` (como lo hace el navegador). */
|
|
17
|
+
const renglonesDe = (texto: string, fuente: string, tam: number, maxAncho: number, mayus?: boolean) => {
|
|
18
|
+
const palabras = (mayus ? texto.toUpperCase() : texto).split(/\s+/).filter(Boolean);
|
|
19
|
+
let n = 1;
|
|
20
|
+
let linea = '';
|
|
21
|
+
for (const w of palabras) {
|
|
22
|
+
const prueba = linea ? `${linea} ${w}` : w;
|
|
23
|
+
if (linea && ancho(prueba, fuente, tam, 800) > maxAncho) {
|
|
24
|
+
n++;
|
|
25
|
+
linea = w;
|
|
26
|
+
} else linea = prueba;
|
|
27
|
+
}
|
|
28
|
+
return n;
|
|
29
|
+
};
|
|
14
30
|
|
|
15
31
|
type P = Record<string, any>;
|
|
16
32
|
|
|
@@ -22,10 +38,16 @@ export function capasDe(tipo: string, p: P, tema: Tema, marca: Marca): Capa[] {
|
|
|
22
38
|
case 'titular': {
|
|
23
39
|
const y = p.y ?? 560;
|
|
24
40
|
const out: Capa[] = [];
|
|
25
|
-
|
|
26
|
-
|
|
41
|
+
const tam = p.tam ?? 104;
|
|
42
|
+
const maxAncho = p.numero != null ? 800 : 920;
|
|
43
|
+
// el texto va centrado en `y`: si se parte en 2+ renglones crece hacia arriba y abajo, así que la etiqueta
|
|
44
|
+
// y el subtítulo se separan medio renglón por cada renglón extra (ajuste por palabras con la fuente real)
|
|
45
|
+
const renglones = renglonesDe(String(p.texto ?? '').replace(/\*/g, ''), tema.fuentes.titulo, tam, maxAncho, tema.tituloMayusculas);
|
|
46
|
+
const extraY = (renglones - 1) * tam * 0.525;
|
|
47
|
+
if (p.etiqueta) out.push({tipo: 'etiqueta', texto: p.etiqueta, x: 80, y: y - 92 - extraY, ancla: 'izquierda', entrada: 'aparecer'});
|
|
48
|
+
out.push({tipo: 'texto', texto: p.texto ?? '', x: 80, y, ancla: 'izquierda', alinear: 'left', tam, maxAncho, entrada: 'subir', en: p.en});
|
|
27
49
|
if (p.numero != null) out.push({tipo: 'insignia', texto: String(p.numero), x: 1000, y, ancla: 'derecha', entrada: 'rebote', en: 0.15});
|
|
28
|
-
if (p.sub) out.push({tipo: 'texto', texto: p.sub, fuente: 'texto', tam: 44, peso: 600, color: tema.colores.suave, x: 80, y: y + 110, ancla: 'izquierda', alinear: 'left', entrada: 'aparecer', en: 0.3});
|
|
50
|
+
if (p.sub) out.push({tipo: 'texto', texto: p.sub, fuente: 'texto', tam: 44, peso: 600, color: tema.colores.suave, x: 80, y: y + 110 + extraY, ancla: 'izquierda', alinear: 'left', entrada: 'aparecer', en: 0.3});
|
|
29
51
|
return [...out, ...extra];
|
|
30
52
|
}
|
|
31
53
|
case 'dato': {
|
package/remotion/motor/tema.ts
CHANGED
|
@@ -252,13 +252,27 @@ export const mezclar = <T,>(a: T, b: any): T => {
|
|
|
252
252
|
return out;
|
|
253
253
|
};
|
|
254
254
|
|
|
255
|
-
/**
|
|
256
|
-
|
|
255
|
+
/**
|
|
256
|
+
* Estilos de papel o cielo (texto oscuro) sobre un ambiente 3D de noche: el texto pasa a claro para que se lea.
|
|
257
|
+
* Los ajustes `tema` del video se aplican después, así que siguen mandando.
|
|
258
|
+
*/
|
|
259
|
+
const SOBRE_NOCHE = {
|
|
260
|
+
textura: 'ninguna',
|
|
261
|
+
colores: {texto: '#FFFFFF', suave: 'rgba(255,255,255,0.6)', tarjeta: '#1C1D24', tarjetaTexto: '#FFFFFF', borde: '#FFFFFF'},
|
|
262
|
+
subtitulos: {color: '#FFFFFF', pendiente: 'rgba(255,255,255,0.5)'},
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Tema final: estilo base (o uno propio del config, que puede heredar con `base`) + ajustes del video.
|
|
267
|
+
* `oscuro`: el video corre sobre un ambiente de noche (capa `ambiente` en el mundo).
|
|
268
|
+
*/
|
|
269
|
+
export function temaDe(estilo: string | undefined, ajustes: Record<string, any> | undefined, propios: Record<string, any> = {}, oscuro = false): Tema {
|
|
257
270
|
const resolver = (nombre: string, n = 0): Tema => {
|
|
258
271
|
if (ESTILOS[nombre]) return ESTILOS[nombre];
|
|
259
272
|
const p = propios[nombre];
|
|
260
273
|
if (p && n < 5) return mezclar(resolver(p.base ?? 'minimal', n + 1), p);
|
|
261
274
|
return ESTILOS.minimal;
|
|
262
275
|
};
|
|
263
|
-
|
|
276
|
+
const base = resolver(estilo ?? 'minimal');
|
|
277
|
+
return mezclar(oscuro && base.colores.texto !== '#FFFFFF' ? mezclar(base, SOBRE_NOCHE) : base, ajustes ?? {});
|
|
264
278
|
}
|