Skip to content

Commit 6d0d510

Browse files
feat: motion-graphics video pipeline (HTML/GSAP → MP4 + GitTools + RenderBackendProtocol) (#27)
Closes #26. Adds programmatic motion-graphics pipeline under praisonai_tools/video/motion_graphics/ with RenderBackendProtocol, HtmlRenderBackend (Playwright + ffmpeg), create_motion_graphics_agent factory, motion_graphics_team preset, and GitTools toolkit. Zero changes to praisonaiagents core SDK. 87/87 unit tests pass.
1 parent 08fed26 commit 6d0d510

20 files changed

+3888
-0
lines changed
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
#!/usr/bin/env python3
2+
"""Example usage of motion graphics video pipeline.
3+
4+
This example demonstrates how to use the motion graphics pipeline to create
5+
videos from natural language prompts using HTML/GSAP animations.
6+
7+
Requirements:
8+
pip install praisonai-tools[video-motion]
9+
playwright install chromium
10+
"""
11+
12+
import asyncio
13+
import tempfile
14+
from pathlib import Path
15+
16+
try:
17+
from praisonai_tools.video.motion_graphics import (
18+
create_motion_graphics_agent,
19+
motion_graphics_team,
20+
HtmlRenderBackend,
21+
RenderOpts
22+
)
23+
from praisonai_tools.tools.git_tools import GitTools
24+
except ImportError as e:
25+
print(f"Import error: {e}")
26+
print("Please install with: pip install praisonai-tools[video-motion]")
27+
exit(1)
28+
29+
30+
async def example_basic_animation():
31+
"""Example 1: Basic motion graphics with simple animations."""
32+
print("Example 1: Creating basic motion graphics animation...")
33+
34+
with tempfile.TemporaryDirectory() as tmpdir:
35+
workspace = Path(tmpdir)
36+
37+
# Create a simple HTML composition
38+
html_content = """
39+
<!DOCTYPE html>
40+
<html>
41+
<head>
42+
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
43+
<style>
44+
body { margin: 0; padding: 0; background: #1a1a1a; overflow: hidden; font-family: Arial, sans-serif; }
45+
#stage { width: 1920px; height: 1080px; position: relative; }
46+
.title { font-size: 72px; color: #ffffff; position: absolute; top: 50%; left: 50%;
47+
transform: translate(-50%, -50%); opacity: 0; }
48+
.subtitle { font-size: 36px; color: #888; position: absolute; top: 60%; left: 50%;
49+
transform: translate(-50%, -50%); opacity: 0; }
50+
</style>
51+
</head>
52+
<body>
53+
<div id="stage" data-duration="3.0">
54+
<div class="title">Motion Graphics</div>
55+
<div class="subtitle">Powered by PraisonAI</div>
56+
</div>
57+
58+
<script>
59+
const tl = gsap.timeline({ paused: true });
60+
61+
// Animate title in
62+
tl.to(".title", {
63+
duration: 1,
64+
opacity: 1,
65+
y: -20,
66+
ease: "power2.out"
67+
})
68+
// Animate subtitle in
69+
.to(".subtitle", {
70+
duration: 0.8,
71+
opacity: 1,
72+
y: -10,
73+
ease: "power2.out"
74+
}, "-=0.3")
75+
// Hold for a moment
76+
.to({}, { duration: 1 })
77+
// Fade out
78+
.to([".title", ".subtitle"], {
79+
duration: 0.5,
80+
opacity: 0,
81+
ease: "power2.in"
82+
});
83+
84+
// Required: Export timeline
85+
window.__timelines = [tl];
86+
</script>
87+
</body>
88+
</html>
89+
"""
90+
91+
# Save HTML file
92+
(workspace / "index.html").write_text(html_content)
93+
94+
# Create backend and render
95+
backend = HtmlRenderBackend()
96+
97+
# Lint first
98+
lint_result = await backend.lint(workspace)
99+
print(f"Lint result: {'✓ Passed' if lint_result.ok else '✗ Failed'}")
100+
if not lint_result.ok:
101+
print(f" Errors: {', '.join(lint_result.messages)}")
102+
103+
# Render video
104+
if lint_result.ok:
105+
render_opts = RenderOpts(
106+
output_name="basic_animation.mp4",
107+
fps=30,
108+
quality="standard"
109+
)
110+
111+
print("Rendering video... (this may take a moment)")
112+
render_result = await backend.render(workspace, render_opts)
113+
114+
if render_result.ok:
115+
print(f"✓ Video rendered successfully!")
116+
print(f" Output: {render_result.output_path}")
117+
print(f" Size: {render_result.size_kb}KB")
118+
else:
119+
print(f"✗ Render failed: {render_result.stderr}")
120+
121+
122+
async def example_git_tools():
123+
"""Example 2: Using GitTools for code exploration."""
124+
print("\nExample 2: Using GitTools for safe git operations...")
125+
126+
with tempfile.TemporaryDirectory() as tmpdir:
127+
git_tools = GitTools(base_dir=tmpdir)
128+
129+
print("Testing repository URL parsing...")
130+
131+
# Test different repository formats
132+
test_repos = [
133+
"octocat/Hello-World", # owner/repo format
134+
"https://github.com/octocat/Hello-World.git", # HTTPS URL
135+
]
136+
137+
for repo_input in test_repos:
138+
try:
139+
url, name = git_tools._parse_repo_input(repo_input)
140+
print(f" {repo_input} -> URL: {url}, Name: {name}")
141+
except Exception as e:
142+
print(f" {repo_input} -> Error: {e}")
143+
144+
print("\nTesting file path safety...")
145+
146+
# Test file path validation
147+
test_paths = [
148+
"README.md", # Safe
149+
"src/main.py", # Safe
150+
"../etc/passwd", # Unsafe
151+
"../../secret.txt", # Unsafe
152+
]
153+
154+
for path in test_paths:
155+
try:
156+
safe_path = git_tools._validate_file_path(path)
157+
print(f" {path} -> ✓ Safe: {safe_path}")
158+
except ValueError as e:
159+
print(f" {path} -> ✗ Unsafe: {e}")
160+
161+
162+
def example_agent_factory():
163+
"""Example 3: Using motion graphics agent factory."""
164+
print("\nExample 3: Creating motion graphics agent...")
165+
166+
try:
167+
# This would normally require praisonaiagents to be installed
168+
agent = create_motion_graphics_agent(
169+
backend="html",
170+
max_retries=3,
171+
llm="claude-sonnet-4"
172+
)
173+
174+
print("✓ Motion graphics agent created successfully!")
175+
print(f" Backend: {agent._motion_graphics_backend.__class__.__name__}")
176+
print(f" Workspace: {agent._motion_graphics_workspace}")
177+
print(f" Max retries: {agent._motion_graphics_max_retries}")
178+
179+
except ImportError as e:
180+
print(f"⚠ Agent creation skipped: {e}")
181+
print(" Install praisonaiagents to use agent features")
182+
183+
184+
def example_team_preset():
185+
"""Example 4: Using motion graphics team preset."""
186+
print("\nExample 4: Creating motion graphics team...")
187+
188+
try:
189+
team = motion_graphics_team(
190+
research=True,
191+
code_exploration=True,
192+
backend="html"
193+
)
194+
195+
print("✓ Motion graphics team created successfully!")
196+
print(f" Agents: {[agent.name for agent in team.agents]}")
197+
print(f" Leader: {team.leader.name}")
198+
print(f" Workspace: {team._motion_graphics_workspace}")
199+
200+
except ImportError as e:
201+
print(f"⚠ Team creation skipped: {e}")
202+
print(" Install praisonaiagents to use team features")
203+
204+
205+
async def main():
206+
"""Run all examples."""
207+
print("Motion Graphics Pipeline Examples")
208+
print("=" * 50)
209+
210+
# Example 1: Basic animation (requires Playwright)
211+
try:
212+
await example_basic_animation()
213+
except ImportError as e:
214+
print(f"Example 1 skipped: {e}")
215+
print("Install with: pip install playwright && playwright install chromium")
216+
except Exception as e:
217+
print(f"Example 1 failed: {e}")
218+
219+
# Example 2: GitTools (no extra dependencies)
220+
await example_git_tools()
221+
222+
# Example 3: Agent factory (requires praisonaiagents)
223+
example_agent_factory()
224+
225+
# Example 4: Team preset (requires praisonaiagents)
226+
example_team_preset()
227+
228+
print("\n" + "=" * 50)
229+
print("Examples complete!")
230+
print("\nNext steps:")
231+
print("1. Install optional dependencies: pip install praisonai-tools[video-motion]")
232+
print("2. Install Playwright: playwright install chromium")
233+
print("3. Install praisonaiagents for agent features")
234+
print("4. Try creating your own motion graphics!")
235+
236+
237+
if __name__ == "__main__":
238+
asyncio.run(main())

examples/motion_graphics_team.yaml

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# Motion Graphics Team Configuration
2+
# This file shows how to configure a motion graphics team using YAML
3+
4+
framework: praisonai
5+
project_name: "Motion Graphics Pipeline"
6+
7+
agents:
8+
- name: "Coordinator"
9+
role: "Team Leader"
10+
backstory: |
11+
You are the motion graphics team coordinator. Your role is to analyze incoming
12+
requests, route them to appropriate specialists, coordinate between team members,
13+
and validate final outputs with strict rules.
14+
goal: |
15+
Route requests effectively, coordinate team work, and ensure high-quality video
16+
output with concrete file paths and proper error handling.
17+
instructions: |
18+
CRITICAL OUTPUT VALIDATION RULES:
19+
- A render succeeded ONLY IF the reply contains a concrete file path AND no error indicators
20+
- Never fabricate file paths or claim success without concrete evidence
21+
- Surface all errors from the Animator
22+
- Stop work after maximum retry budget is exceeded
23+
24+
- name: "Researcher"
25+
role: "Research Specialist"
26+
backstory: |
27+
You are a research specialist who gathers information about topics in user requests.
28+
You search the web for relevant facts, concepts, and examples, then summarize findings
29+
in a brief that the Animator can use for on-screen content.
30+
goal: |
31+
Provide focused research that can be turned into visual motion graphics elements.
32+
tools:
33+
- search_web
34+
35+
- name: "CodeExplorer"
36+
role: "Code Analysis Specialist"
37+
backstory: |
38+
You are a code exploration specialist who can clone and explore git repositories
39+
on-demand. You read source code, understand implementations, and extract key
40+
algorithms, data structures, and concepts for visualization.
41+
goal: |
42+
Extract essential programming concepts that can be animated visually.
43+
tools:
44+
- GitTools
45+
instructions: |
46+
IMPORTANT: You are READ-ONLY. Never write or modify code.
47+
Always validate file paths to prevent directory traversal.
48+
49+
- name: "Animator"
50+
role: "Motion Graphics Creator"
51+
backstory: |
52+
You are a motion graphics specialist who creates HTML/CSS/GSAP compositions
53+
that render to high-quality MP4 videos. You follow strict authoring guidelines
54+
and iterate on render failures.
55+
goal: |
56+
Create deterministic, frame-perfect HTML/GSAP compositions for video export.
57+
tools:
58+
- FileTools
59+
- RenderTools
60+
instructions: |
61+
Follow the motion graphics authoring skill guide precisely:
62+
- Use deterministic animations only (no Math.random, no infinite loops)
63+
- Export timelines as window.__timelines = [tl]
64+
- Add data-duration attributes
65+
- Use 1920x1080 viewport
66+
- Test with lint and render tools
67+
- Iterate on failures with maximum retry limit
68+
69+
tasks:
70+
- description: |
71+
Create a 30-second motion graphics video explaining the concept requested by the user.
72+
73+
Process:
74+
1. Coordinator routes to Researcher if topic research is needed
75+
2. Coordinator routes to CodeExplorer if code analysis is needed
76+
3. Coordinator always routes to Animator for final composition and rendering
77+
4. Animator creates HTML/GSAP composition following authoring guidelines
78+
5. Animator tests with lint and render tools
79+
6. Animator iterates on failures up to maximum retries
80+
7. Coordinator validates final output has concrete file path
81+
82+
expected_output: |
83+
A rendered MP4 file with:
84+
- Concrete file path (e.g., /renders/video_123.mp4)
85+
- Video duration matching user request
86+
- High-quality motion graphics explaining the concept
87+
- No render errors or fabricated paths
88+
89+
agent: "Coordinator"
90+
91+
# Team configuration
92+
team:
93+
type: "hierarchical"
94+
manager: "Coordinator"
95+
verbose: true
96+
97+
# Tool configuration
98+
tools:
99+
- name: "GitTools"
100+
config:
101+
base_dir: "./repos"
102+
103+
- name: "RenderTools"
104+
config:
105+
backend: "html"
106+
workspace: "./renders"
107+
max_retries: 3
108+
109+
- name: "FileTools"
110+
config:
111+
base_dir: "./workspace"
112+
113+
# Output configuration
114+
output:
115+
format: "mp4"
116+
quality: "standard"
117+
fps: 30
118+
workspace: "./motion_graphics_output"

0 commit comments

Comments
 (0)