Skip to content

Commit cd2176b

Browse files
committed
add examples
1 parent 1d4e913 commit cd2176b

File tree

2 files changed

+331
-0
lines changed

2 files changed

+331
-0
lines changed

examples/avatar_live.py

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
"""
2+
Avatar Live Example
3+
4+
This example demonstrates how to use the avatar-live model to animate an avatar image.
5+
The avatar can be animated with audio input (microphone or audio file).
6+
7+
Usage:
8+
# With audio file:
9+
DECART_API_KEY=your-key python avatar_live.py avatar.png audio.mp3
10+
11+
# With just avatar image (will wait for audio):
12+
DECART_API_KEY=your-key python avatar_live.py avatar.png
13+
14+
Requirements:
15+
pip install decart[realtime]
16+
"""
17+
18+
import asyncio
19+
import os
20+
import sys
21+
from pathlib import Path
22+
23+
try:
24+
from aiortc.contrib.media import MediaPlayer, MediaRecorder
25+
except ImportError:
26+
print("aiortc is required for this example.")
27+
print("Install with: pip install decart[realtime]")
28+
sys.exit(1)
29+
30+
from decart import DecartClient, models
31+
32+
33+
async def main():
34+
api_key = os.getenv("DECART_API_KEY")
35+
if not api_key:
36+
print("Error: DECART_API_KEY environment variable not set")
37+
print("Usage: DECART_API_KEY=your-key python avatar_live.py <avatar_image> [audio_file]")
38+
return
39+
40+
if len(sys.argv) < 2:
41+
print("Usage: python avatar_live.py <avatar_image> [audio_file]")
42+
print("")
43+
print("Arguments:")
44+
print(" avatar_image - Path to avatar image (PNG, JPG)")
45+
print(" audio_file - Optional: Path to audio file for the avatar to speak")
46+
print("")
47+
print("Examples:")
48+
print(" python avatar_live.py avatar.png")
49+
print(" python avatar_live.py avatar.png speech.mp3")
50+
return
51+
52+
avatar_image = sys.argv[1]
53+
if not os.path.exists(avatar_image):
54+
print(f"Error: Avatar image not found: {avatar_image}")
55+
return
56+
57+
audio_file = sys.argv[2] if len(sys.argv) > 2 else None
58+
if audio_file and not os.path.exists(audio_file):
59+
print(f"Error: Audio file not found: {audio_file}")
60+
return
61+
62+
print(f"🖼️ Avatar image: {avatar_image}")
63+
if audio_file:
64+
print(f"🔊 Audio file: {audio_file}")
65+
66+
# Load audio if provided
67+
audio_track = None
68+
if audio_file:
69+
print("Loading audio file...")
70+
player = MediaPlayer(audio_file)
71+
if player.audio:
72+
audio_track = player.audio
73+
print("✓ Audio loaded")
74+
else:
75+
print("⚠️ Warning: No audio stream found in file, continuing without audio")
76+
77+
try:
78+
from decart.realtime.client import RealtimeClient
79+
from decart.realtime.types import RealtimeConnectOptions, AvatarOptions
80+
except ImportError:
81+
print("Error: Realtime API not available")
82+
print("Install with: pip install decart[realtime]")
83+
return
84+
85+
print("\nCreating Decart client...")
86+
async with DecartClient(api_key=api_key) as client:
87+
model = models.realtime("avatar-live")
88+
print(f"Using model: {model.name}")
89+
90+
frame_count = 0
91+
recorder = None
92+
output_file = Path(f"output_avatar_live.mp4")
93+
94+
def on_remote_stream(track):
95+
nonlocal frame_count, recorder
96+
frame_count += 1
97+
if frame_count % 25 == 0:
98+
print(f"📹 Received {frame_count} frames...")
99+
100+
if recorder is None:
101+
print(f"💾 Recording to {output_file}")
102+
recorder = MediaRecorder(str(output_file))
103+
recorder.addTrack(track)
104+
asyncio.create_task(recorder.start())
105+
106+
def on_connection_change(state):
107+
print(f"🔄 Connection state: {state}")
108+
109+
def on_error(error):
110+
print(f"❌ Error: {error.__class__.__name__} - {error.message}")
111+
112+
print("\nConnecting to Avatar Live API...")
113+
print("(Sending avatar image...)")
114+
115+
try:
116+
realtime_client = await RealtimeClient.connect(
117+
base_url=client.base_url,
118+
api_key=client.api_key,
119+
local_track=audio_track, # Can be None if no audio
120+
options=RealtimeConnectOptions(
121+
model=model,
122+
on_remote_stream=on_remote_stream,
123+
avatar=AvatarOptions(avatar_image=Path(avatar_image)),
124+
),
125+
)
126+
127+
realtime_client.on("connection_change", on_connection_change)
128+
realtime_client.on("error", on_error)
129+
130+
print("✓ Connected!")
131+
print(f"Session ID: {realtime_client.session_id}")
132+
133+
if audio_file:
134+
print("\nPlaying audio through avatar...")
135+
print("(The avatar will animate based on the audio)")
136+
else:
137+
print("\nNo audio provided - avatar will be static")
138+
print("You can update the avatar image dynamically using set_image()")
139+
140+
print("\nPress Ctrl+C to stop and save the recording...")
141+
142+
# Demo: Update avatar image after 5 seconds (if you want to test set_image)
143+
# Uncomment the following to test dynamic image updates:
144+
# await asyncio.sleep(5)
145+
# print("Updating avatar image...")
146+
# await realtime_client.set_image(Path("new_avatar.png"))
147+
# print("✓ Avatar image updated!")
148+
149+
try:
150+
while True:
151+
await asyncio.sleep(1)
152+
except KeyboardInterrupt:
153+
print(f"\n\n✓ Received {frame_count} frames total")
154+
155+
finally:
156+
if recorder:
157+
try:
158+
print(f"💾 Saving video to {output_file}...")
159+
await asyncio.sleep(0.5)
160+
await recorder.stop()
161+
print(f"✓ Video saved to {output_file}")
162+
except Exception as e:
163+
print(f"⚠️ Warning: Could not save video cleanly: {e}")
164+
print(" Video file may be incomplete")
165+
166+
except Exception as e:
167+
print(f"\n❌ Connection failed: {e}")
168+
import traceback
169+
traceback.print_exc()
170+
171+
finally:
172+
if "realtime_client" in locals():
173+
print("\nDisconnecting...")
174+
await realtime_client.disconnect()
175+
print("✓ Disconnected")
176+
177+
178+
if __name__ == "__main__":
179+
asyncio.run(main())

examples/video_restyle.py

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
"""
2+
Video Restyle Example
3+
4+
This example demonstrates how to use the lucy-restyle-v2v model to restyle a video
5+
using either a text prompt OR a reference image.
6+
7+
Usage:
8+
# With text prompt:
9+
DECART_API_KEY=your-key python video_restyle.py input.mp4 --prompt "anime style"
10+
11+
# With reference image:
12+
DECART_API_KEY=your-key python video_restyle.py input.mp4 --reference style.png
13+
14+
Requirements:
15+
pip install decart
16+
"""
17+
18+
import asyncio
19+
import argparse
20+
import os
21+
import sys
22+
from pathlib import Path
23+
24+
from decart import DecartClient, models
25+
26+
27+
async def main():
28+
parser = argparse.ArgumentParser(
29+
description="Restyle a video using text prompt or reference image"
30+
)
31+
parser.add_argument("video", help="Path to input video file")
32+
parser.add_argument(
33+
"--prompt", "-p",
34+
help="Text prompt describing the style (e.g., 'anime style', 'oil painting')"
35+
)
36+
parser.add_argument(
37+
"--reference", "-r",
38+
help="Path to reference image for style transfer"
39+
)
40+
parser.add_argument(
41+
"--output", "-o",
42+
help="Output file path (default: output_restyle.mp4)"
43+
)
44+
parser.add_argument(
45+
"--seed", "-s",
46+
type=int,
47+
help="Random seed for reproducibility"
48+
)
49+
parser.add_argument(
50+
"--enhance",
51+
action="store_true",
52+
default=True,
53+
help="Enhance the prompt (only with --prompt, default: True)"
54+
)
55+
parser.add_argument(
56+
"--no-enhance",
57+
action="store_true",
58+
help="Disable prompt enhancement"
59+
)
60+
61+
args = parser.parse_args()
62+
63+
# Validate arguments
64+
if not args.prompt and not args.reference:
65+
print("Error: Must provide either --prompt or --reference")
66+
parser.print_help()
67+
sys.exit(1)
68+
69+
if args.prompt and args.reference:
70+
print("Error: Cannot use both --prompt and --reference")
71+
print(" Please choose one or the other")
72+
sys.exit(1)
73+
74+
api_key = os.getenv("DECART_API_KEY")
75+
if not api_key:
76+
print("Error: DECART_API_KEY environment variable not set")
77+
sys.exit(1)
78+
79+
video_path = Path(args.video)
80+
if not video_path.exists():
81+
print(f"Error: Video file not found: {video_path}")
82+
sys.exit(1)
83+
84+
if args.reference:
85+
ref_path = Path(args.reference)
86+
if not ref_path.exists():
87+
print(f"Error: Reference image not found: {ref_path}")
88+
sys.exit(1)
89+
90+
output_path = args.output or f"output_restyle_{video_path.stem}.mp4"
91+
92+
print("=" * 50)
93+
print("Video Restyle")
94+
print("=" * 50)
95+
print(f"Input video: {video_path}")
96+
if args.prompt:
97+
print(f"Style: Text prompt - '{args.prompt}'")
98+
print(f"Enhance prompt: {not args.no_enhance}")
99+
else:
100+
print(f"Style: Reference image - {args.reference}")
101+
print(f"Output: {output_path}")
102+
if args.seed:
103+
print(f"Seed: {args.seed}")
104+
print("=" * 50)
105+
106+
async with DecartClient(api_key=api_key) as client:
107+
# Build options
108+
options = {
109+
"model": models.video("lucy-restyle-v2v"),
110+
"data": video_path,
111+
}
112+
113+
if args.prompt:
114+
options["prompt"] = args.prompt
115+
options["enhance_prompt"] = not args.no_enhance
116+
else:
117+
options["reference_image"] = Path(args.reference)
118+
119+
if args.seed:
120+
options["seed"] = args.seed
121+
122+
def on_status_change(job):
123+
status_emoji = {
124+
"pending": "⏳",
125+
"processing": "🔄",
126+
"completed": "✅",
127+
"failed": "❌",
128+
}
129+
emoji = status_emoji.get(job.status, "•")
130+
print(f"{emoji} Status: {job.status}")
131+
132+
options["on_status_change"] = on_status_change
133+
134+
print("\nSubmitting job...")
135+
result = await client.queue.submit_and_poll(options)
136+
137+
if result.status == "failed":
138+
print(f"\n❌ Job failed: {result.error}")
139+
sys.exit(1)
140+
141+
print(f"\n✅ Job completed!")
142+
print(f"💾 Saving to {output_path}...")
143+
144+
with open(output_path, "wb") as f:
145+
f.write(result.data)
146+
147+
print(f"✓ Video saved to {output_path}")
148+
print(f" Size: {len(result.data) / 1024 / 1024:.2f} MB")
149+
150+
151+
if __name__ == "__main__":
152+
asyncio.run(main())

0 commit comments

Comments
 (0)