Add streaming video compositor with sexp interpreter
- New streaming/ module for real-time video processing: - compositor.py: Main streaming compositor with cycle-crossfade - sexp_executor.py: Executes compiled sexp recipes in real-time - sexp_interp.py: Full S-expression interpreter for SLICE_ON Lambda - recipe_adapter.py: Bridges recipes to streaming compositor - sources.py: Video source with ffmpeg streaming - audio.py: Real-time audio analysis (energy, beats) - output.py: Preview (mpv) and file output with audio muxing - New templates/: - cycle-crossfade.sexp: Smooth zoom-based video cycling - process-pair.sexp: Dual-clip processing with effects - Key features: - Videos cycle in input-videos order (not definition order) - Cumulative whole-spin rotation - Zero-weight sources skip processing - Live audio-reactive effects - New effects: blend_multi for weighted layer compositing - Updated primitives and interpreter for streaming compatibility Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -793,6 +793,35 @@ class Interpreter:
|
||||
return list(self.effects.values())[-1]
|
||||
return None
|
||||
|
||||
def load_effect_from_string(self, sexp_content: str, effect_name: str = None) -> EffectDefinition:
|
||||
"""Load an effect definition from an S-expression string.
|
||||
|
||||
Args:
|
||||
sexp_content: The S-expression content as a string
|
||||
effect_name: Optional name hint (used if effect doesn't define its own name)
|
||||
|
||||
Returns:
|
||||
The loaded EffectDefinition
|
||||
"""
|
||||
expr = parse(sexp_content)
|
||||
|
||||
# Handle multiple top-level expressions
|
||||
if isinstance(expr, list) and expr and isinstance(expr[0], list):
|
||||
for e in expr:
|
||||
self.eval(e)
|
||||
else:
|
||||
self.eval(expr)
|
||||
|
||||
# Return the effect if we can find it by name
|
||||
if effect_name and effect_name in self.effects:
|
||||
return self.effects[effect_name]
|
||||
|
||||
# Return the most recently loaded effect
|
||||
if self.effects:
|
||||
return list(self.effects.values())[-1]
|
||||
|
||||
return None
|
||||
|
||||
def run_effect(self, name: str, frame, params: Dict[str, Any],
|
||||
state: Dict[str, Any]) -> tuple:
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user