morfeo 0.5.0 → 0.5.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Morfeo
2
2
 
3
- > **Documentación: [morfeo-docs-two.vercel.app](https://morfeo-docs-two.vercel.app/)** · Ejemplos: [github.com/UreckChan/morfeo](https://github.com/UreckChan/morfeo) · Versiones: [historial](https://morfeo-docs-two.vercel.app/historial)
3
+ > **Documentación: [morfeo.ureck.dev](https://morfeo.ureck.dev/)** · Ejemplos: [github.com/UreckChan/morfeo](https://github.com/UreckChan/morfeo) · Versiones: [historial](https://morfeo.ureck.dev/historial)
4
4
 
5
5
  Genera videos verticales animados (Shorts, TikTok, Reels) desde código: escribes las escenas y Morfeo agrega
6
6
  la voz, la música, los efectos de sonido, los subtítulos, las animaciones y la miniatura.
@@ -66,6 +66,9 @@ export function revisarEstructura(v, d) {
66
66
  // formato largo: explicador de maqueta (o `formato: 'largo'` en el video)
67
67
  const formula = formulaDe(v.formula);
68
68
  const largo = v.estilo === 'maqueta' || v.formato === 'largo' || (formula?.duracion[1] ?? 0) > 90;
69
+ // a qué se compara: los explicadores de maqueta (100–130 s) o la duración de la fórmula (biografía 90–180 s…)
70
+ const enLargo = v.estilo === 'maqueta' || !formula ? 'en maqueta' : `en la fórmula "${formula.id}"`;
71
+ const rangoLargo = v.estilo === 'maqueta' || !formula ? [100, 130] : formula.duracion;
69
72
 
70
73
  // 1) gancho
71
74
  const dicho = String(primera.say ?? '').trim();
@@ -79,8 +82,8 @@ export function revisarEstructura(v, d) {
79
82
  const es = d?.escenas?.filter((e) => e.tipo !== 'cta') ?? [];
80
83
  if (es.length) {
81
84
  const dur = d.duracion;
82
- if (largo && dur > 140) add('duracion', 'idea', `Dura ${Math.round(dur)} s: los explicadores de maqueta de referencia van de 100 a 130 s.`);
83
- else if (largo && dur < 60) add('duracion', 'idea', `Dura ${Math.round(dur)} s: en maqueta los de referencia duran 100–130 s; hay espacio para otra pregunta y su respuesta.`);
85
+ if (largo && dur > rangoLargo[1] + 10) add('duracion', 'idea', `Dura ${Math.round(dur)} s: ${enLargo} los de referencia van de ${rangoLargo[0]} a ${rangoLargo[1]} s.`);
86
+ else if (largo && dur < 60) add('duracion', 'idea', `Dura ${Math.round(dur)} s: ${enLargo} los de referencia duran ${rangoLargo[0]}–${rangoLargo[1]} s; hay espacio para otra pregunta y su respuesta.`);
84
87
  else if (!largo && dur > 75) add('duracion', 'idea', `Dura ${Math.round(dur)} s: los de referencia rondan 60 s. Si no es una serie o una historia, recorta (o usa estilo maqueta para un explicador largo).`);
85
88
  if (dur < 20) add('duracion', 'idea', `Dura ${Math.round(dur)} s: un poco más de desarrollo (una pregunta y su respuesta) suele retener mejor.`);
86
89
  const e0 = es[0];
@@ -91,7 +94,7 @@ export function revisarEstructura(v, d) {
91
94
  // algo que cambie a media escena: capas con `en`, cámara con rango, toma con movimiento, o un mundo vivo
92
95
  const capas = s.capas ?? [];
93
96
  const cambia = capas.some((c) => c.en != null && c.en !== 0 && c.en !== 'inicio') || Array.isArray(s.camara?.zoom) || s.toma?.a || capas.some((c) => c.mover || c.cambios);
94
- if (largo && voz > 4.5) add('ritmo', 'idea', `Escena ${i + 1}: ${voz.toFixed(1)} s en una sola toma; en maqueta la cámara corta cada 2–3 s. Pártela en dos tomas.`);
97
+ if (largo && voz > 4.5) add('ritmo', 'idea', `Escena ${i + 1}: ${voz.toFixed(1)} s en una sola toma; ${enLargo} la cámara corta cada 2–3 s. Pártela en dos tomas.`);
95
98
  else if (voz > 6 && !cambia && !v.mundo?.length) add('ritmo', 'aviso', `Escena ${i + 1}: ${voz.toFixed(1)} s de voz sin que nada cambie. Pártela en dos o haz que algo entre a media frase.`);
96
99
  else if (voz > 9) add('ritmo', 'idea', `Escena ${i + 1}: ${voz.toFixed(1)} s de voz en una sola escena; en los de referencia ninguna idea pasa de ~5 s.`);
97
100
  });
@@ -110,7 +113,7 @@ export function revisarEstructura(v, d) {
110
113
  // 5) habla de tú
111
114
  const todo = cuerpo.map((s) => s.say ?? '').join(' ');
112
115
  const n = palabrasDe(todo).length;
113
- const tu = (todo.match(/\b(tú|tu|tus|te|ti|contigo|imagina|imaginas|tienes|eres|puedes|sabes|sabías|crees|conoces|harías|pagarías)\b/gi) ?? []).length;
116
+ const tu = (todo.match(/(?<!\p{L})(tú|tu|tus|te|ti|contigo|imagina|imaginas|tienes|eres|puedes|sabes|sabías|crees|conoces|harías|pagarías|jugabas|tuviste|tuyo|tuya)(?!\p{L})/giu) ?? []).length;
114
117
  if (n > 40 && tu === 0) add('tu', 'idea', 'No le habla al espectador ni una vez: una frase en segunda persona ("imagina que tú…") lo mete en la historia.');
115
118
  // 6) lo propio de la fórmula del video (lib/formulas.mjs)
116
119
  for (const t of revisarFormula(v)) add('formula', 'idea', t);
package/lib/render.mjs CHANGED
@@ -208,6 +208,23 @@ export async function preparar({proyecto = process.cwd(), ids, log = console.log
208
208
  return {p, publico, marca, extras, datos, creditos, fallidos};
209
209
  }
210
210
 
211
+ // ¿El video dibuja con WebGL? Capas 3D del motor o componentes del proyecto que usan Three.js / Lienzo3D.
212
+ export function usaWebGL(p, id) {
213
+ const v = p.videos[id];
214
+ if (!v) return false;
215
+ const texto = JSON.stringify(v);
216
+ if (/"tipo":"(modelo|escena3d)"/.test(texto)) return true;
217
+ for (const [nombre, ruta] of Object.entries(p.componentes ?? {})) {
218
+ if (!texto.includes(`"nombre":"${nombre}"`)) continue;
219
+ try {
220
+ if (/Lienzo3D|ThreeCanvas|@react-three|from ['"]three['"]/.test(fs.readFileSync(ruta, 'utf8'))) return true;
221
+ } catch {
222
+ return true;
223
+ }
224
+ }
225
+ return false;
226
+ }
227
+
211
228
  /**
212
229
  * Genera los videos. Sin ids: los que aún no existen en la carpeta de salida.
213
230
  * @param {{proyecto?: string, ids?: string[], todos?: boolean, log?: (m: string) => void, alProgreso?: (id: string, p: number) => void}} o
@@ -273,7 +290,10 @@ export async function render({proyecto = process.cwd(), ids, todos = false, solo
273
290
  const d0 = datos[id];
274
291
  const finPrevia = typeof previa === 'number' ? previa : Math.min(8, Math.max(4, (d0.escenas[1] ?? d0.escenas[0]).t0 + (d0.escenas[1] ?? d0.escenas[0]).dur));
275
292
  let gl = p.render.gl;
276
- let concurrencia = concurrenciaBase;
293
+ // WebGL con GPU y muchas pestañas: alguna se queda "pegada" y sus cuadros salen con rótulos o subtítulos de
294
+ // cuadros anteriores (medido en Windows + RTX 3060: 9 pestañas → 0–54 cuadros malos de 270; 4 → 41–81; 2–3 → 0
295
+ // en 24 pruebas, casi igual de rápido). Solo se limita si el video usa 3D y nadie fijó la concurrencia.
296
+ let concurrencia = gl === 'angle' && p.render.concurrencia == null && usaWebGL(p, id) ? Math.min(concurrenciaBase, 3) : concurrenciaBase;
277
297
  let composition;
278
298
  // hasta 3 intentos: si el navegador truena, se baja la concurrencia y, con GPU, se pasa a software
279
299
  for (let intento = 1; ; intento++) {
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "morfeo",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
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",
7
7
  "author": "Uriel Gómez",
8
- "homepage": "https://morfeo-docs-two.vercel.app/",
8
+ "homepage": "https://morfeo.ureck.dev/",
9
9
  "repository": {
10
10
  "type": "git",
11
11
  "url": "git+https://github.com/UreckChan/morfeo.git"
@@ -8,7 +8,7 @@ Comandos de referencia: `npx morfeo ayuda`. Tipos de todo lo que existe: `node_m
8
8
 
9
9
  ## Cuando diga «Configúrame» (o es la primera vez)
10
10
 
11
- 1. Revisa que haya Node 20+ (`node -v`). Si falta, dile cómo instalarlo y espera.
11
+ 1. Revisa que haya Node 18.17+ (`node -v`). Si falta, dile cómo instalarlo y espera.
12
12
  2. `npm install` en esta carpeta.
13
13
  3. `npx morfeo instalar --si` (Python, voces gratis, navegador; tarda unos minutos la primera vez). Si algo falla,
14
14
  lee el mensaje, arréglalo y vuelve a correrlo. `npx morfeo doctor` dice qué falta.