Sistema de Expresiones Dinámicas
El sistema de videomaker soporta expresiones matemáticas para calcular dinámicamente los valores de start, length, y también en effects y transitions, permitiendo referencias a otros clips y operaciones complejas.
Sintaxis Básica
Referencias a Clips
Puedes referenciar cualquier clip usando clipX donde X es el índice (order) del clip:
{
"start": "clip0.end",
"length": "clip1.duration"
}
Propiedades Disponibles
Cada clip tiene las siguientes propiedades:
clipX.start- Tiempo de inicio del clip X (en segundos)clipX.end- Tiempo de fin del clip X (start + length)clipX.length- Duración del clip XclipX.duration- Alias delength
Atajo prev
Para mayor conveniencia, puedes usar prev para referenciar el clip anterior:
{
"start": "prev.end",
"length": "prev.duration"
}
Esto es equivalente a clip0.end si el clip actual es el clip 1.
Variable Especial length
Dentro de effects y transitions, puedes usar la variable length para referenciar la duración del clip actual:
{
"effects": [{
"type": "zoom",
"duration": "length" // Duración igual al clip
}]
}
También puedes hacer operaciones:
{
"effects": [{
"type": "zoom",
"duration": "length * 0.5" // Mitad de la duración del clip
}]
}
Operaciones Matemáticas
Operadores Soportados
+Suma:clip0.end + 2-Resta:clip1.start - 0.5*Multiplicación:clip2.duration * 2/División:clip3.length / 2()Paréntesis para prioridad:(clip1.duration + clip2.duration) / 2
Funciones Matemáticas
min(a, b)- Mínimo entre dos valoresmax(a, b)- Máximo entre dos valoresabs(x)- Valor absolutofloor(x)- Redondeo hacia abajoceil(x)- Redondeo hacia arribaround(x)- Redondeo al entero más cercano
Ejemplos
Ejemplo 1: Secuencia Simple
{
"timeline": [
{
"asset": { "type": "image", "src": "intro.png" },
"start": 0,
"length": 5
},
{
"asset": { "type": "video", "src": "video.mp4" },
"start": "prev.end",
"length": 10
},
{
"asset": { "type": "image", "src": "outro.png" },
"start": "prev.end",
"length": 5
}
]
}
Resultado:
- Clip 0: start=0, length=5 (0-5s)
- Clip 1: start=5, length=10 (5-15s)
- Clip 2: start=15, length=5 (15-20s)
Ejemplo 2: Overlap (Superposición)
{
"timeline": [
{
"asset": { "type": "video", "src": "background.mp4" },
"start": 0,
"length": 30
},
{
"asset": { "type": "text", "text": "Title" },
"start": "clip0.start + 2",
"length": 5
},
{
"asset": { "type": "audio", "src": "music.mp3" },
"start": "clip0.start",
"length": "clip0.duration"
}
]
}
Resultado:
- Clip 0: start=0, length=30 (video de fondo)
- Clip 1: start=2, length=5 (título aparece 2s después)
- Clip 2: start=0, length=30 (audio sincronizado con video)
Ejemplo 3: Gaps (Espacios)
{
"timeline": [
{
"asset": { "type": "image", "src": "scene1.png" },
"start": 0,
"length": 5
},
{
"asset": { "type": "image", "src": "scene2.png" },
"start": "prev.end + 1",
"length": 5
},
{
"asset": { "type": "image", "src": "scene3.png" },
"start": "prev.end + 1",
"length": 5
}
]
}
Resultado:
- Clip 0: 0-5s
- Clip 1: 6-11s (1 segundo de gap)
- Clip 2: 12-17s (1 segundo de gap)
Ejemplo 4: TTS con Audio Dinámico
{
"timeline": [
{
"asset": {
"type": "tts",
"text": "Hola mundo",
"voice": "Lucia"
},
"start": 0,
"length": "{{audio.duration}}"
},
{
"asset": { "type": "image", "src": "image.png" },
"start": "prev.end",
"length": 5
}
]
}
Resultado:
- Clip 0: start=0, length=duración_real_del_audio (ej: 1.2s)
- Clip 1: start=1.2, length=5
Ejemplo 5: Sincronización Compleja
{
"timeline": [
{
"asset": { "type": "video", "src": "intro.mp4" },
"start": 0,
"length": 3
},
{
"asset": { "type": "video", "src": "main.mp4" },
"start": "prev.end",
"length": 10
},
{
"asset": { "type": "image", "src": "logo.png" },
"start": "clip0.start",
"length": "clip0.duration + clip1.duration"
},
{
"asset": { "type": "video", "src": "outro.mp4" },
"start": "max(clip1.end, clip2.end)",
"length": 3
}
]
}
Resultado:
- Clip 0: 0-3s (intro)
- Clip 1: 3-13s (main video)
- Clip 2: 0-13s (logo superpuesto durante intro + main)
- Clip 3: 13-16s (outro empieza cuando termina el último clip)
Ejemplo 6: Cálculos Avanzados
{
"timeline": [
{
"asset": { "type": "video", "src": "video1.mp4" },
"start": 0,
"length": 10
},
{
"asset": { "type": "video", "src": "video2.mp4" },
"start": "prev.end",
"length": 8
},
{
"asset": { "type": "text", "text": "Middle Point" },
"start": "(clip0.duration + clip1.duration) / 2",
"length": 2
},
{
"asset": { "type": "image", "src": "thumbnail.png" },
"start": "clip2.start - 1",
"length": "clip2.duration + 2"
}
]
}
Resultado:
- Clip 0: 0-10s
- Clip 1: 10-18s
- Clip 2: 9s, length=2s (punto medio entre ambos videos)
- Clip 3: 8-12s (1s antes del punto medio, hasta 1s después)
Compatibilidad con Formato Antiguo
El sistema sigue siendo compatible con el formato legacy:
{
"start": 0,
"length": "{{audio.duration}}"
}
Esto automáticamente se convierte a la duración real del audio procesado.
Valores Literales
También puedes usar números directamente:
{
"start": 5,
"length": 10
}
O valores decimales:
{
"start": 2.5,
"length": 1.75
}
Notas Importantes
Orden de Procesamiento: Los clips se procesan en orden (por
order). Puedes referenciar clips anteriores sin problema.Referencias Circulares: Evita referencias circulares (ej: clip1 depende de clip2, y clip2 depende de clip1).
Clips No Encontrados: Si referencias un clip que no existe o aún no está resuelto, se usarán valores por defecto.
Precisión: Todos los cálculos se hacen en segundos con precisión de punto flotante.
Validación: Las expresiones inválidas se detectan y se usan valores por defecto para evitar fallos.
API de Validación
Puedes validar expresiones antes de enviarlas:
import { expressionResolver } from '~/services/expression-resolver.server';
const result = expressionResolver.validateExpression("clip0.end + 2");
if (!result.valid) {
console.error('Invalid expression:', result.error);
}
Ejemplo 7: Expresiones en Effects y Transitions
{
"timeline": [
{
"asset": { "type": "image", "src": "photo.jpg" },
"start": 0,
"length": "clip6.end - clip0.end", // Duración dinámica
"effects": [
{
"type": "zoom",
"from": 1,
"to": 1.2,
"duration": "length", // El efecto dura lo mismo que el clip
"easing": "easeInOut"
}
],
"transition": {
"in": [{
"type": "fade",
"duration": "length * 0.1" // Fade dura 10% del clip
}]
}
},
{
"asset": { "type": "video", "src": "video.mp4" },
"start": "prev.end - 0.5",
"length": 10,
"effects": [
{
"type": "blur",
"from": 0,
"to": 5,
"duration": "length / 2", // Blur solo en la primera mitad
"easing": "linear"
}
]
}
]
}
Resultado:
- Clip 0: Zoom que dura exactamente lo mismo que el clip (duración dinámica)
- Clip 0: Fade in que dura 10% de la duración del clip
- Clip 1: Blur que dura 5 segundos (mitad de 10s)
Ejemplo 8: Sincronización Avanzada con Expresiones
{
"timeline": [
{
"asset": {
"type": "tts",
"text": "Texto largo para narrar",
"voice": "Lucia"
},
"start": 4,
"length": "audio.length"
},
{
"asset": { "type": "image", "src": "background.jpg" },
"start": 0,
"length": "clip0.end", // Dura hasta que termina el audio
"effects": [
{
"type": "zoom",
"from": 1,
"to": 1.3,
"duration": "clip0.length", // Zoom dura lo mismo que el audio
"easing": "easeInOut"
}
]
}
]
}
Resolución de Problemas
Expresión no válida
Error: Expression "clip0.endd + 2" did not evaluate to a valid number
Solución: Verifica que los nombres de propiedades estén correctos (start, end, length, duration).
Clip no resuelto
Warning: Expression "clip5.end" references clip not yet resolved
Solución: Asegúrate de que el clip referenciado existe y tiene un order menor al clip actual.
Resultado inesperado
Si obtienes un resultado diferente al esperado, verifica:
- El orden de los clips (campo
order) - Las duraciones procesadas de clips TTS/audio
- Las operaciones matemáticas y precedencia (usa paréntesis si es necesario)
Campos que Soportan Expresiones
Nivel Clip
start- Tiempo de inicio del cliplength- Duración del clip
Dentro de Effects
duration- Duración del efecto- Cualquier campo numérico (ej:
from,to,startTime)
Dentro de Transitions
duration- Duración de la transiciónfromX,fromY,toX,toY- Posiciones- Cualquier campo numérico
Variable Especial
length- Solo disponible dentro de effects/transitions, referencia la duración del clip actual