Replace batch DAG system with streaming architecture
- Remove legacy_tasks.py, hybrid_state.py, render.py - Remove old task modules (analyze, execute, execute_sexp, orchestrate) - Add streaming interpreter from test repo - Add sexp_effects with primitives and video effects - Add streaming Celery task with CID-based asset resolution - Support both CID and friendly name references for assets - Add .dockerignore to prevent local clones from conflicting Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
25
templates/crossfade-zoom.sexp
Normal file
25
templates/crossfade-zoom.sexp
Normal file
@@ -0,0 +1,25 @@
|
||||
;; Crossfade with Zoom Transition
|
||||
;;
|
||||
;; Macro for transitioning between two frames with a zoom effect.
|
||||
;; Active frame zooms out while next frame zooms in.
|
||||
;;
|
||||
;; Required context:
|
||||
;; - zoom effect must be loaded
|
||||
;; - blend effect must be loaded
|
||||
;;
|
||||
;; Parameters:
|
||||
;; active-frame: current frame
|
||||
;; next-frame: frame to transition to
|
||||
;; fade-amt: transition progress (0 = all active, 1 = all next)
|
||||
;;
|
||||
;; Usage:
|
||||
;; (include :path "../templates/crossfade-zoom.sexp")
|
||||
;; ...
|
||||
;; (crossfade-zoom active-frame next-frame 0.5)
|
||||
|
||||
(defmacro crossfade-zoom (active-frame next-frame fade-amt)
|
||||
(let [active-zoom (+ 1.0 fade-amt)
|
||||
active-zoomed (zoom active-frame :amount active-zoom)
|
||||
next-zoom (+ 0.1 (* fade-amt 0.9))
|
||||
next-zoomed (zoom next-frame :amount next-zoom)]
|
||||
(blend active-zoomed next-zoomed :opacity fade-amt)))
|
||||
65
templates/cycle-crossfade.sexp
Normal file
65
templates/cycle-crossfade.sexp
Normal file
@@ -0,0 +1,65 @@
|
||||
;; cycle-crossfade template
|
||||
;;
|
||||
;; Generalized cycling zoom-crossfade for any number of video layers.
|
||||
;; Cycles through videos with smooth zoom-based crossfade transitions.
|
||||
;;
|
||||
;; Parameters:
|
||||
;; beat-data - beat analysis node (drives timing)
|
||||
;; input-videos - list of video nodes to cycle through
|
||||
;; init-clen - initial cycle length in beats
|
||||
;;
|
||||
;; NOTE: The parameter is named "input-videos" (not "videos") because
|
||||
;; template substitution replaces param names everywhere in the AST.
|
||||
;; The planner's _expand_slice_on injects env["videos"] at plan time,
|
||||
;; so (len videos) inside the lambda references that injected value.
|
||||
|
||||
(deftemplate cycle-crossfade
|
||||
(beat-data input-videos init-clen)
|
||||
|
||||
(slice-on beat-data
|
||||
:videos input-videos
|
||||
:init {:cycle 0 :beat 0 :clen init-clen}
|
||||
:fn (lambda [acc i start end]
|
||||
(let [beat (get acc "beat")
|
||||
clen (get acc "clen")
|
||||
active (get acc "cycle")
|
||||
n (len videos)
|
||||
phase3 (* beat 3)
|
||||
wt (lambda [p]
|
||||
(let [prev (mod (+ p (- n 1)) n)]
|
||||
(if (= active p)
|
||||
(if (< phase3 clen) 1.0
|
||||
(if (< phase3 (* clen 2))
|
||||
(- 1.0 (* (/ (- phase3 clen) clen) 1.0))
|
||||
0.0))
|
||||
(if (= active prev)
|
||||
(if (< phase3 clen) 0.0
|
||||
(if (< phase3 (* clen 2))
|
||||
(* (/ (- phase3 clen) clen) 1.0)
|
||||
1.0))
|
||||
0.0))))
|
||||
zm (lambda [p]
|
||||
(let [prev (mod (+ p (- n 1)) n)]
|
||||
(if (= active p)
|
||||
;; Active video: normal -> zoom out during transition -> tiny
|
||||
(if (< phase3 clen) 1.0
|
||||
(if (< phase3 (* clen 2))
|
||||
(+ 1.0 (* (/ (- phase3 clen) clen) 1.0))
|
||||
0.1))
|
||||
(if (= active prev)
|
||||
;; Incoming video: tiny -> zoom in during transition -> normal
|
||||
(if (< phase3 clen) 0.1
|
||||
(if (< phase3 (* clen 2))
|
||||
(+ 0.1 (* (/ (- phase3 clen) clen) 0.9))
|
||||
1.0))
|
||||
0.1))))
|
||||
new-acc (if (< (+ beat 1) clen)
|
||||
(dict :cycle active :beat (+ beat 1) :clen clen)
|
||||
(dict :cycle (mod (+ active 1) n) :beat 0
|
||||
:clen (+ 40 (mod (* i 7) 41))))]
|
||||
{:layers (map (lambda [p]
|
||||
{:video p :effects [{:effect zoom :amount (zm p)}]})
|
||||
(range 0 n))
|
||||
:compose {:effect blend_multi :mode "alpha"
|
||||
:weights (map (lambda [p] (wt p)) (range 0 n))}
|
||||
:acc new-acc}))))
|
||||
112
templates/process-pair.sexp
Normal file
112
templates/process-pair.sexp
Normal file
@@ -0,0 +1,112 @@
|
||||
;; process-pair template
|
||||
;;
|
||||
;; Reusable video-pair processor: takes a single video source, creates two
|
||||
;; clips (A and B) with opposite rotations and sporadic effects, blends them,
|
||||
;; and applies a per-pair slow rotation driven by a beat scan.
|
||||
;;
|
||||
;; All sporadic triggers (invert, hue-shift, ascii) and pair-level controls
|
||||
;; (blend opacity, rotation) are defined internally using seed offsets.
|
||||
;;
|
||||
;; Parameters:
|
||||
;; video - source video node
|
||||
;; energy - energy analysis node (drives rotation/zoom amounts)
|
||||
;; beat-data - beat analysis node (drives sporadic triggers)
|
||||
;; rng - RNG object from (make-rng seed) for auto-derived seeds
|
||||
;; rot-dir - initial rotation direction: 1 (clockwise) or -1 (anti-clockwise)
|
||||
;; rot-a/b - rotation ranges for clip A/B (e.g. [0 45])
|
||||
;; zoom-a/b - zoom ranges for clip A/B (e.g. [1 1.5])
|
||||
|
||||
(deftemplate process-pair
|
||||
(video energy beat-data rng rot-dir rot-a rot-b zoom-a zoom-b)
|
||||
|
||||
;; --- Sporadic triggers for clip A ---
|
||||
|
||||
;; Invert: 10% chance per beat, lasts 1-5 beats
|
||||
(def inv-a (scan beat-data :rng rng :init 0
|
||||
:step (if (> acc 0) (- acc 1) (if (< (rand) 0.1) (rand-int 1 5) 0))
|
||||
:emit (if (> acc 0) 1 0)))
|
||||
|
||||
;; Hue shift: 10% chance, random hue 30-330 deg, lasts 1-5 beats
|
||||
(def hue-a (scan beat-data :rng rng :init (dict :rem 0 :hue 0)
|
||||
:step (if (> rem 0)
|
||||
(dict :rem (- rem 1) :hue hue)
|
||||
(if (< (rand) 0.1)
|
||||
(dict :rem (rand-int 1 5) :hue (rand-range 30 330))
|
||||
(dict :rem 0 :hue 0)))
|
||||
:emit (if (> rem 0) hue 0)))
|
||||
|
||||
;; ASCII art: 5% chance, lasts 1-3 beats
|
||||
(def ascii-a (scan beat-data :rng rng :init 0
|
||||
:step (if (> acc 0) (- acc 1) (if (< (rand) 0.05) (rand-int 1 3) 0))
|
||||
:emit (if (> acc 0) 1 0)))
|
||||
|
||||
;; --- Sporadic triggers for clip B (offset seeds) ---
|
||||
|
||||
(def inv-b (scan beat-data :rng rng :init 0
|
||||
:step (if (> acc 0) (- acc 1) (if (< (rand) 0.1) (rand-int 1 5) 0))
|
||||
:emit (if (> acc 0) 1 0)))
|
||||
|
||||
(def hue-b (scan beat-data :rng rng :init (dict :rem 0 :hue 0)
|
||||
:step (if (> rem 0)
|
||||
(dict :rem (- rem 1) :hue hue)
|
||||
(if (< (rand) 0.1)
|
||||
(dict :rem (rand-int 1 5) :hue (rand-range 30 330))
|
||||
(dict :rem 0 :hue 0)))
|
||||
:emit (if (> rem 0) hue 0)))
|
||||
|
||||
(def ascii-b (scan beat-data :rng rng :init 0
|
||||
:step (if (> acc 0) (- acc 1) (if (< (rand) 0.05) (rand-int 1 3) 0))
|
||||
:emit (if (> acc 0) 1 0)))
|
||||
|
||||
;; --- Pair-level controls ---
|
||||
|
||||
;; Internal A/B blend: randomly show A (0), both (0.5), or B (1), every 1-11 beats
|
||||
(def pair-mix (scan beat-data :rng rng
|
||||
:init (dict :rem 0 :opacity 0.5)
|
||||
:step (if (> rem 0)
|
||||
(dict :rem (- rem 1) :opacity opacity)
|
||||
(dict :rem (rand-int 1 11) :opacity (* (rand-int 0 2) 0.5)))
|
||||
:emit opacity))
|
||||
|
||||
;; Per-pair rotation: one full rotation every 20-30 beats, alternating direction
|
||||
(def pair-rot (scan beat-data :rng rng
|
||||
:init (dict :beat 0 :clen 25 :dir rot-dir :angle 0)
|
||||
:step (if (< (+ beat 1) clen)
|
||||
(dict :beat (+ beat 1) :clen clen :dir dir
|
||||
:angle (+ angle (* dir (/ 360 clen))))
|
||||
(dict :beat 0 :clen (rand-int 20 30) :dir (* dir -1)
|
||||
:angle angle))
|
||||
:emit angle))
|
||||
|
||||
;; --- Clip A processing ---
|
||||
(def clip-a (-> video (segment :start 0 :duration (bind energy duration))))
|
||||
(def rotated-a (-> clip-a
|
||||
(effect rotate :angle (bind energy values :range rot-a))
|
||||
(effect zoom :amount (bind energy values :range zoom-a))
|
||||
(effect invert :amount (bind inv-a values))
|
||||
(effect hue_shift :degrees (bind hue-a values))
|
||||
;; ASCII disabled - too slow without GPU
|
||||
;; (effect ascii_art
|
||||
;; :char_size (bind energy values :range [4 32])
|
||||
;; :mix (bind ascii-a values))
|
||||
))
|
||||
|
||||
;; --- Clip B processing ---
|
||||
(def clip-b (-> video (segment :start 0 :duration (bind energy duration))))
|
||||
(def rotated-b (-> clip-b
|
||||
(effect rotate :angle (bind energy values :range rot-b))
|
||||
(effect zoom :amount (bind energy values :range zoom-b))
|
||||
(effect invert :amount (bind inv-b values))
|
||||
(effect hue_shift :degrees (bind hue-b values))
|
||||
;; ASCII disabled - too slow without GPU
|
||||
;; (effect ascii_art
|
||||
;; :char_size (bind energy values :range [4 32])
|
||||
;; :mix (bind ascii-b values))
|
||||
))
|
||||
|
||||
;; --- Blend A+B and apply pair rotation ---
|
||||
(-> rotated-a
|
||||
(effect blend rotated-b
|
||||
:mode "alpha" :opacity (bind pair-mix values) :resize_mode "fit")
|
||||
(effect rotate
|
||||
:angle (bind pair-rot values))))
|
||||
28
templates/scan-oscillating-spin.sexp
Normal file
28
templates/scan-oscillating-spin.sexp
Normal file
@@ -0,0 +1,28 @@
|
||||
;; Oscillating Spin Scan
|
||||
;;
|
||||
;; Accumulates rotation angle on each beat, reversing direction
|
||||
;; periodically for an oscillating effect.
|
||||
;;
|
||||
;; Required context:
|
||||
;; - music: audio analyzer from (streaming:make-audio-analyzer ...)
|
||||
;;
|
||||
;; Provides scan: spin
|
||||
;; Bind with: (bind spin :angle) ;; cumulative rotation angle
|
||||
;;
|
||||
;; Behavior:
|
||||
;; - Rotates 14.4 degrees per beat (completes 360 in 25 beats)
|
||||
;; - After 20-30 beats, reverses direction
|
||||
;; - Creates a swinging/oscillating rotation effect
|
||||
;;
|
||||
;; Usage:
|
||||
;; (include :path "../templates/scan-oscillating-spin.sexp")
|
||||
;;
|
||||
;; In frame:
|
||||
;; (rotate frame :angle (bind spin :angle))
|
||||
|
||||
(scan spin (streaming:audio-beat music t)
|
||||
:init {:angle 0 :dir 1 :left 25}
|
||||
:step (if (> left 0)
|
||||
(dict :angle (+ angle (* dir 14.4)) :dir dir :left (- left 1))
|
||||
(dict :angle angle :dir (* dir -1)
|
||||
:left (+ 20 (mod (streaming:audio-beat-count music t) 11)))))
|
||||
41
templates/scan-ripple-drops.sexp
Normal file
41
templates/scan-ripple-drops.sexp
Normal file
@@ -0,0 +1,41 @@
|
||||
;; Beat-Triggered Ripple Drops Scan
|
||||
;;
|
||||
;; Creates random ripple drops triggered by audio beats.
|
||||
;; Each drop has a random center position and duration.
|
||||
;;
|
||||
;; Required context:
|
||||
;; - music: audio analyzer from (streaming:make-audio-analyzer ...)
|
||||
;; - core primitives loaded
|
||||
;;
|
||||
;; Provides scan: ripple-state
|
||||
;; Bind with: (bind ripple-state :gate) ;; 0 or 1
|
||||
;; (bind ripple-state :cx) ;; center x (0-1)
|
||||
;; (bind ripple-state :cy) ;; center y (0-1)
|
||||
;;
|
||||
;; Parameters:
|
||||
;; trigger-chance: probability per beat (default 0.15)
|
||||
;; min-duration: minimum beats (default 1)
|
||||
;; max-duration: maximum beats (default 15)
|
||||
;;
|
||||
;; Usage:
|
||||
;; (include :path "../templates/scan-ripple-drops.sexp")
|
||||
;; ;; Uses default: 15% chance, 1-15 beat duration
|
||||
;;
|
||||
;; In frame:
|
||||
;; (let [rip-gate (bind ripple-state :gate)
|
||||
;; rip-amp (* rip-gate (core:map-range e 0 1 5 50))]
|
||||
;; (ripple frame
|
||||
;; :amplitude rip-amp
|
||||
;; :center_x (bind ripple-state :cx)
|
||||
;; :center_y (bind ripple-state :cy)))
|
||||
|
||||
(scan ripple-state (streaming:audio-beat music t)
|
||||
:init {:gate 0 :cx 0.5 :cy 0.5 :left 0}
|
||||
:step (if (> left 0)
|
||||
(dict :gate 1 :cx cx :cy cy :left (- left 1))
|
||||
(if (< (core:rand) 0.15)
|
||||
(dict :gate 1
|
||||
:cx (+ 0.2 (* (core:rand) 0.6))
|
||||
:cy (+ 0.2 (* (core:rand) 0.6))
|
||||
:left (+ 1 (mod (streaming:audio-beat-count music t) 15)))
|
||||
(dict :gate 0 :cx 0.5 :cy 0.5 :left 0))))
|
||||
22
templates/standard-effects.sexp
Normal file
22
templates/standard-effects.sexp
Normal file
@@ -0,0 +1,22 @@
|
||||
;; Standard Effects Bundle
|
||||
;;
|
||||
;; Loads commonly-used video effects.
|
||||
;; Include after primitives are loaded.
|
||||
;;
|
||||
;; Effects provided:
|
||||
;; - rotate: rotation by angle
|
||||
;; - zoom: scale in/out
|
||||
;; - blend: alpha blend two frames
|
||||
;; - ripple: water ripple distortion
|
||||
;; - invert: color inversion
|
||||
;; - hue_shift: hue rotation
|
||||
;;
|
||||
;; Usage:
|
||||
;; (include :path "../templates/standard-effects.sexp")
|
||||
|
||||
(effect rotate :path "../sexp_effects/effects/rotate.sexp")
|
||||
(effect zoom :path "../sexp_effects/effects/zoom.sexp")
|
||||
(effect blend :path "../sexp_effects/effects/blend.sexp")
|
||||
(effect ripple :path "../sexp_effects/effects/ripple.sexp")
|
||||
(effect invert :path "../sexp_effects/effects/invert.sexp")
|
||||
(effect hue_shift :path "../sexp_effects/effects/hue_shift.sexp")
|
||||
14
templates/standard-primitives.sexp
Normal file
14
templates/standard-primitives.sexp
Normal file
@@ -0,0 +1,14 @@
|
||||
;; Standard Primitives Bundle
|
||||
;;
|
||||
;; Loads all commonly-used primitive libraries.
|
||||
;; Include this at the top of streaming recipes.
|
||||
;;
|
||||
;; Usage:
|
||||
;; (include :path "../templates/standard-primitives.sexp")
|
||||
|
||||
(require-primitives "geometry")
|
||||
(require-primitives "core")
|
||||
(require-primitives "image")
|
||||
(require-primitives "blending")
|
||||
(require-primitives "color_ops")
|
||||
(require-primitives "streaming")
|
||||
72
templates/stream-process-pair.sexp
Normal file
72
templates/stream-process-pair.sexp
Normal file
@@ -0,0 +1,72 @@
|
||||
;; stream-process-pair template (streaming-compatible)
|
||||
;;
|
||||
;; Macro for processing a video source pair with full effects.
|
||||
;; Reads source, applies A/B effects (rotate, zoom, invert, hue), blends,
|
||||
;; and applies pair-level rotation.
|
||||
;;
|
||||
;; Required context (must be defined in calling scope):
|
||||
;; - sources: array of video sources
|
||||
;; - pair-configs: array of {:dir :rot-a :rot-b :zoom-a :zoom-b} configs
|
||||
;; - pair-states: array from (bind pairs :states)
|
||||
;; - now: current time (t)
|
||||
;; - e: audio energy (0-1)
|
||||
;;
|
||||
;; Required effects (must be loaded):
|
||||
;; - rotate, zoom, invert, hue_shift, blend
|
||||
;;
|
||||
;; Usage:
|
||||
;; (include :path "../templates/stream-process-pair.sexp")
|
||||
;; ...in frame pipeline...
|
||||
;; (let [pair-states (bind pairs :states)
|
||||
;; now t
|
||||
;; e (streaming:audio-energy music now)]
|
||||
;; (process-pair 0)) ;; process source at index 0
|
||||
|
||||
(require-primitives "core")
|
||||
|
||||
(defmacro process-pair (src-idx)
|
||||
(let [src (nth sources src-idx)
|
||||
frame (streaming:source-read src now)
|
||||
cfg (nth pair-configs src-idx)
|
||||
state (nth pair-states src-idx)
|
||||
|
||||
;; Get state values (invert uses countdown > 0)
|
||||
inv-a-active (if (> (get state :inv-a) 0) 1 0)
|
||||
inv-b-active (if (> (get state :inv-b) 0) 1 0)
|
||||
;; Hue is active only when countdown > 0
|
||||
hue-a-val (if (> (get state :hue-a) 0) (get state :hue-a-val) 0)
|
||||
hue-b-val (if (> (get state :hue-b) 0) (get state :hue-b-val) 0)
|
||||
mix-opacity (get state :mix)
|
||||
pair-rot-angle (* (get state :angle) (get cfg :dir))
|
||||
|
||||
;; Get config values for energy-mapped ranges
|
||||
rot-a-max (get cfg :rot-a)
|
||||
rot-b-max (get cfg :rot-b)
|
||||
zoom-a-max (get cfg :zoom-a)
|
||||
zoom-b-max (get cfg :zoom-b)
|
||||
|
||||
;; Energy-driven rotation and zoom
|
||||
rot-a (core:map-range e 0 1 0 rot-a-max)
|
||||
rot-b (core:map-range e 0 1 0 rot-b-max)
|
||||
zoom-a (core:map-range e 0 1 1 zoom-a-max)
|
||||
zoom-b (core:map-range e 0 1 1 zoom-b-max)
|
||||
|
||||
;; Apply effects to clip A
|
||||
clip-a (-> frame
|
||||
(rotate :angle rot-a)
|
||||
(zoom :amount zoom-a)
|
||||
(invert :amount inv-a-active)
|
||||
(hue_shift :degrees hue-a-val))
|
||||
|
||||
;; Apply effects to clip B
|
||||
clip-b (-> frame
|
||||
(rotate :angle rot-b)
|
||||
(zoom :amount zoom-b)
|
||||
(invert :amount inv-b-active)
|
||||
(hue_shift :degrees hue-b-val))
|
||||
|
||||
;; Blend A+B
|
||||
blended (blend clip-a clip-b :opacity mix-opacity)]
|
||||
|
||||
;; Apply pair-level rotation
|
||||
(rotate blended :angle pair-rot-angle)))
|
||||
Reference in New Issue
Block a user