ladybug11 commited on
Commit
89661b8
Β·
1 Parent(s): aa371c2

Add ElevenLabs voice + multiple variations

Browse files
Files changed (2) hide show
  1. app.py +236 -79
  2. requirements.txt +2 -1
app.py CHANGED
@@ -6,14 +6,16 @@ import tempfile
6
  from openai import OpenAI
7
  from smolagents import CodeAgent, MCPClient, tool
8
  from huggingface_hub import InferenceClient
9
- from moviepy.editor import VideoFileClip, ImageClip, CompositeVideoClip
10
  from PIL import Image, ImageDraw, ImageFont
11
  import textwrap
12
  import numpy as np
 
13
 
14
  # Initialize clients
15
  openai_client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
16
  PEXELS_API_KEY = os.getenv("PEXELS_API_KEY")
 
17
 
18
  # Initialize MCP Client (connecting to existing MCP server)
19
  try:
@@ -190,15 +192,62 @@ def search_pexels_video_tool(style: str, niche: str) -> dict:
190
  }
191
 
192
  @tool
193
- def create_quote_video_tool(video_url: str, quote_text: str, output_path: str) -> dict:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  """
195
  Create a final quote video by overlaying text on the background video.
196
  Uses PIL/Pillow for text rendering (works on Hugging Face Spaces).
 
197
 
198
  Args:
199
  video_url: URL of the background video from Pexels
200
  quote_text: The quote text to overlay
201
  output_path: Path where to save the final video
 
202
 
203
  Returns:
204
  Dictionary with success status and output path
@@ -304,6 +353,17 @@ def create_quote_video_tool(video_url: str, quote_text: str, output_path: str) -
304
  # Step 5: Composite video with text
305
  final_video = CompositeVideoClip([video, text_clip])
306
 
 
 
 
 
 
 
 
 
 
 
 
307
  # Step 6: Export final video
308
  final_video.write_videofile(
309
  output_path,
@@ -343,10 +403,10 @@ def initialize_agent():
343
 
344
  # Create agent with custom tools
345
  agent = CodeAgent(
346
- tools=[generate_quote_tool, search_pexels_video_tool, create_quote_video_tool],
347
  model=model,
348
  additional_authorized_imports=["requests", "openai", "random", "tempfile", "os"],
349
- max_steps=10
350
  )
351
 
352
  # Add MCP client if available
@@ -360,10 +420,11 @@ def initialize_agent():
360
  # Initialize agent
361
  agent, agent_error = initialize_agent()
362
 
363
- def mcp_agent_pipeline(niche, style):
364
  """
365
  MCP-POWERED AUTONOMOUS AGENT PIPELINE
366
  Uses smolagents with proper MCP server integration
 
367
  """
368
 
369
  status_log = []
@@ -372,85 +433,120 @@ def mcp_agent_pipeline(niche, style):
372
  if agent_error:
373
  status_log.append(f"❌ Agent initialization failed: {agent_error}")
374
  status_log.append("\nπŸ”„ Falling back to direct tool execution...\n")
375
- return fallback_pipeline(niche, style)
376
 
377
  try:
378
  # STEP 1: Agent receives task
379
  status_log.append("πŸ“‹ **TASK RECEIVED:**")
380
  status_log.append(f" β†’ Generate {niche} quote with {style} aesthetic")
381
- status_log.append(f" β†’ Find matching video")
382
- status_log.append(f" β†’ Create final quote video\n")
 
 
383
 
384
  # STEP 2: Agent executes quote generation
385
  status_log.append("🧠 **MCP TOOL: generate_quote_tool**")
386
  quote = generate_quote_tool(niche, style)
387
 
388
  if "Error" in quote:
389
- return "\n".join(status_log) + f"\n❌ Failed: {quote}", None, None
390
 
391
- status_log.append(f" βœ… Generated: \"{quote}\"\n")
392
-
393
- # STEP 3: Agent executes video search
394
- status_log.append("πŸ” **MCP TOOL: search_pexels_video_tool**")
395
- video_result = search_pexels_video_tool(style, niche)
396
 
397
- if not video_result["success"]:
398
- error_msg = video_result.get("error", "Unknown error")
399
- return "\n".join(status_log) + f"\n❌ Video search failed: {error_msg}", None, None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
 
401
- status_log.append(f" βœ… Found video: {video_result['search_query']}")
402
- status_log.append(f" πŸ“₯ Video URL: {video_result['video_url']}\n")
403
 
404
- # STEP 4: Agent creates final video with text overlay
405
- status_log.append("🎬 **MCP TOOL: create_quote_video_tool**")
406
- status_log.append(" ⏳ Creating video with text overlay...")
407
 
408
- # Create output directory if it doesn't exist
409
  output_dir = "/tmp/quote_videos"
410
  os.makedirs(output_dir, exist_ok=True)
411
 
412
- # Generate unique filename
413
  import time
414
- output_filename = f"quote_video_{int(time.time())}.mp4"
415
- output_path = os.path.join(output_dir, output_filename)
416
 
417
- # Create the video
418
- creation_result = create_quote_video_tool(
419
- video_result["video_url"],
420
- quote,
421
- output_path
422
- )
 
 
 
 
 
 
 
 
 
 
423
 
424
- if not creation_result["success"]:
425
- status_log.append(f" ❌ Video creation failed: {creation_result['message']}")
426
- status_log.append("\nπŸ“Ί **PREVIEW MODE:**")
427
- status_log.append(" Showing background video preview instead")
428
- return "\n".join(status_log), video_result["video_url"], None
429
 
430
- status_log.append(f" βœ… Video created successfully!\n")
431
 
432
- # STEP 5: MCP Server integration status
433
  status_log.append("πŸ”— **MCP SERVER STATUS:**")
434
  if mcp_enabled:
435
  status_log.append(" βœ… Connected to: abidlabs-mcp-tools.hf.space")
436
- status_log.append(" βœ… Additional MCP tools available")
437
  else:
438
  status_log.append(" ⚠️ MCP server connection pending")
439
  status_log.append("")
440
 
441
- # STEP 6: Success!
442
  status_log.append("✨ **PIPELINE COMPLETE!**")
443
- status_log.append(f" πŸ“± Original video: {video_result['pexels_url']}")
444
- status_log.append(f" 🎬 Final video ready for download!")
 
 
445
 
446
  final_status = "\n".join(status_log)
447
- return final_status, video_result["video_url"], creation_result["output_path"]
448
 
449
  except Exception as e:
450
  status_log.append(f"\n❌ Pipeline error: {str(e)}")
451
- return "\n".join(status_log), None, None
452
 
453
- def fallback_pipeline(niche, style):
454
  """Fallback pipeline if MCP agent fails"""
455
  status_log = []
456
  status_log.append("πŸ”„ **FALLBACK MODE (Direct Tool Execution)**\n")
@@ -460,54 +556,85 @@ def fallback_pipeline(niche, style):
460
  quote = generate_quote_tool(niche, style)
461
 
462
  if "Error" in quote:
463
- return "\n".join(status_log) + f"\n❌ {quote}", None, None
464
 
465
- status_log.append(f" βœ… Quote: \"{quote}\"\n")
466
 
467
- # Search video
468
- status_log.append("πŸ” Searching for video...")
469
- video_result = search_pexels_video_tool(style, niche)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
470
 
471
- if not video_result["success"]:
472
- return "\n".join(status_log) + f"\n❌ {video_result.get('error', 'Failed')}", None, None
 
 
 
 
 
 
 
 
473
 
474
- status_log.append(f" βœ… Found: {video_result['search_query']}\n")
475
 
476
- # Create video
477
- status_log.append("🎬 Creating final video...")
478
  output_dir = "/tmp/quote_videos"
479
  os.makedirs(output_dir, exist_ok=True)
480
 
481
  import time
482
- output_filename = f"quote_video_{int(time.time())}.mp4"
483
- output_path = os.path.join(output_dir, output_filename)
484
 
485
- creation_result = create_quote_video_tool(
486
- video_result["video_url"],
487
- quote,
488
- output_path
489
- )
 
 
 
 
 
 
 
 
490
 
491
- if not creation_result["success"]:
492
- status_log.append(f" ❌ {creation_result['message']}")
493
- return "\n".join(status_log), video_result["video_url"], None
494
 
495
- status_log.append(" βœ… Video created!\n")
496
  status_log.append("🎬 **COMPLETE!**")
497
 
498
- return "\n".join(status_log), video_result["video_url"], creation_result["output_path"]
499
 
500
  # Gradio Interface
501
  with gr.Blocks(title="AIQuoteClipGenerator - MCP Edition", theme=gr.themes.Soft()) as demo:
502
  gr.Markdown("""
503
  # 🎬 AIQuoteClipGenerator
504
- ### MCP-Powered Autonomous AI Agent
505
 
506
  **MCP Integration Features:**
507
  - πŸ”— **MCP Server:** Connected to smolagents framework
508
- - πŸ› οΈ **Custom Tools:** Quote generation + Video search + Video creation
509
  - πŸ€– **Agent Reasoning:** Autonomous task execution
510
  - ⚑ **Tool Orchestration:** Intelligent pipeline management
 
 
511
  """)
512
 
513
  with gr.Row():
@@ -539,6 +666,21 @@ with gr.Blocks(title="AIQuoteClipGenerator - MCP Edition", theme=gr.themes.Soft(
539
  value="Cinematic"
540
  )
541
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
542
  generate_btn = gr.Button("πŸ€– Run MCP Agent", variant="primary", size="lg")
543
 
544
  with gr.Column():
@@ -550,30 +692,45 @@ with gr.Blocks(title="AIQuoteClipGenerator - MCP Edition", theme=gr.themes.Soft(
550
  gr.Markdown("### πŸŽ₯ Background Video Preview")
551
  preview_video = gr.Video(label="Original Pexels Video")
552
 
553
- with gr.Column():
554
- gr.Markdown("### ✨ Final Quote Video")
555
- final_video = gr.Video(label="Download Your Video")
 
 
 
 
 
 
 
 
 
 
556
 
557
  gr.Markdown("""
558
  ---
 
 
 
 
 
559
  ### ✨ MCP Implementation
560
  - βœ… **smolagents Framework** - Proper MCP integration
561
- - βœ… **Custom MCP Tools** - Quote generation, video search & video creation
562
  - βœ… **CodeAgent** - Autonomous reasoning and execution
563
  - βœ… **MCP Client** - Connected to external MCP servers
564
- - βœ… **MoviePy Processing** - Text overlay with professional styling
565
- - 🚧 **ElevenLabs Integration** - Voice narration (future)
566
 
567
  ### πŸ† Hackathon: MCP 1st Birthday
568
  **Track:** Track 2 - MCP in Action
569
  **Category:** Productivity Tools
570
- **Built with:** Gradio + smolagents + OpenAI + Pexels + MoviePy + MCP
571
  """)
572
 
573
  generate_btn.click(
574
  mcp_agent_pipeline,
575
- inputs=[niche, style],
576
- outputs=[output, preview_video, final_video]
577
  )
578
 
579
  if __name__ == "__main__":
 
6
  from openai import OpenAI
7
  from smolagents import CodeAgent, MCPClient, tool
8
  from huggingface_hub import InferenceClient
9
+ from moviepy.editor import VideoFileClip, ImageClip, CompositeVideoClip, AudioFileClip
10
  from PIL import Image, ImageDraw, ImageFont
11
  import textwrap
12
  import numpy as np
13
+ from elevenlabs import ElevenLabs, VoiceSettings
14
 
15
  # Initialize clients
16
  openai_client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
17
  PEXELS_API_KEY = os.getenv("PEXELS_API_KEY")
18
+ elevenlabs_client = ElevenLabs(api_key=os.getenv("ELEVENLABS_API_KEY"))
19
 
20
  # Initialize MCP Client (connecting to existing MCP server)
21
  try:
 
192
  }
193
 
194
  @tool
195
+ def generate_voice_narration_tool(quote_text: str, output_path: str) -> dict:
196
+ """
197
+ Generate voice narration for the quote using ElevenLabs.
198
+
199
+ Args:
200
+ quote_text: The quote text to narrate
201
+ output_path: Path where to save the audio file
202
+
203
+ Returns:
204
+ Dictionary with success status and output path
205
+ """
206
+
207
+ try:
208
+ # Generate audio using ElevenLabs
209
+ audio = elevenlabs_client.text_to_speech.convert(
210
+ text=quote_text,
211
+ voice_id="pNInz6obpgDQGcFmaJgB", # Adam voice - clear and motivational
212
+ model_id="eleven_multilingual_v2",
213
+ voice_settings=VoiceSettings(
214
+ stability=0.5,
215
+ similarity_boost=0.75,
216
+ style=0.5,
217
+ use_speaker_boost=True
218
+ )
219
+ )
220
+
221
+ # Save audio to file
222
+ with open(output_path, 'wb') as f:
223
+ for chunk in audio:
224
+ f.write(chunk)
225
+
226
+ return {
227
+ "success": True,
228
+ "output_path": output_path,
229
+ "message": "Voice narration created successfully!"
230
+ }
231
+
232
+ except Exception as e:
233
+ return {
234
+ "success": False,
235
+ "output_path": None,
236
+ "message": f"Error creating voice: {str(e)}"
237
+ }
238
+
239
+ @tool
240
+ def create_quote_video_tool(video_url: str, quote_text: str, output_path: str, audio_path: str = None) -> dict:
241
  """
242
  Create a final quote video by overlaying text on the background video.
243
  Uses PIL/Pillow for text rendering (works on Hugging Face Spaces).
244
+ Optionally adds voice narration audio.
245
 
246
  Args:
247
  video_url: URL of the background video from Pexels
248
  quote_text: The quote text to overlay
249
  output_path: Path where to save the final video
250
+ audio_path: Optional path to audio file for voice narration
251
 
252
  Returns:
253
  Dictionary with success status and output path
 
353
  # Step 5: Composite video with text
354
  final_video = CompositeVideoClip([video, text_clip])
355
 
356
+ # Step 5.5: Add voice narration if provided
357
+ if audio_path and os.path.exists(audio_path):
358
+ try:
359
+ audio_clip = AudioFileClip(audio_path)
360
+ # Use the shorter duration between video and audio
361
+ audio_duration = min(audio_clip.duration, final_video.duration)
362
+ audio_clip = audio_clip.subclip(0, audio_duration)
363
+ final_video = final_video.set_audio(audio_clip)
364
+ except Exception as audio_error:
365
+ print(f"Warning: Could not add audio: {audio_error}")
366
+
367
  # Step 6: Export final video
368
  final_video.write_videofile(
369
  output_path,
 
403
 
404
  # Create agent with custom tools
405
  agent = CodeAgent(
406
+ tools=[generate_quote_tool, search_pexels_video_tool, generate_voice_narration_tool, create_quote_video_tool],
407
  model=model,
408
  additional_authorized_imports=["requests", "openai", "random", "tempfile", "os"],
409
+ max_steps=15
410
  )
411
 
412
  # Add MCP client if available
 
420
  # Initialize agent
421
  agent, agent_error = initialize_agent()
422
 
423
+ def mcp_agent_pipeline(niche, style, num_variations=3, add_voice=True):
424
  """
425
  MCP-POWERED AUTONOMOUS AGENT PIPELINE
426
  Uses smolagents with proper MCP server integration
427
+ Generates multiple video variations with optional voice narration
428
  """
429
 
430
  status_log = []
 
433
  if agent_error:
434
  status_log.append(f"❌ Agent initialization failed: {agent_error}")
435
  status_log.append("\nπŸ”„ Falling back to direct tool execution...\n")
436
+ return fallback_pipeline(niche, style, num_variations, add_voice)
437
 
438
  try:
439
  # STEP 1: Agent receives task
440
  status_log.append("πŸ“‹ **TASK RECEIVED:**")
441
  status_log.append(f" β†’ Generate {niche} quote with {style} aesthetic")
442
+ status_log.append(f" β†’ Create {num_variations} video variations")
443
+ if add_voice:
444
+ status_log.append(f" β†’ Add voice narration with ElevenLabs")
445
+ status_log.append("")
446
 
447
  # STEP 2: Agent executes quote generation
448
  status_log.append("🧠 **MCP TOOL: generate_quote_tool**")
449
  quote = generate_quote_tool(niche, style)
450
 
451
  if "Error" in quote:
452
+ return "\n".join(status_log) + f"\n❌ Failed: {quote}", None, []
453
 
454
+ status_log.append(f" βœ… Generated: \"{quote[:100]}...\"" if len(quote) > 100 else f" βœ… Generated: \"{quote}\"\n")
 
 
 
 
455
 
456
+ # STEP 3: Generate voice narration if requested
457
+ audio_path = None
458
+ if add_voice:
459
+ status_log.append("🎀 **MCP TOOL: generate_voice_narration_tool**")
460
+ status_log.append(" ⏳ Creating AI voice narration...")
461
+
462
+ audio_dir = "/tmp/quote_audio"
463
+ os.makedirs(audio_dir, exist_ok=True)
464
+
465
+ import time
466
+ audio_filename = f"narration_{int(time.time())}.mp3"
467
+ audio_path = os.path.join(audio_dir, audio_filename)
468
+
469
+ voice_result = generate_voice_narration_tool(quote, audio_path)
470
+
471
+ if voice_result["success"]:
472
+ status_log.append(f" βœ… Voice narration created!\n")
473
+ else:
474
+ status_log.append(f" ⚠️ Voice creation failed, continuing without audio\n")
475
+ audio_path = None
476
+
477
+ # STEP 4: Search for multiple videos
478
+ status_log.append(f"πŸ” **MCP TOOL: search_pexels_video_tool (x{num_variations})**")
479
+ status_log.append(f" ⏳ Finding {num_variations} different videos...")
480
+
481
+ video_results = []
482
+ for i in range(num_variations):
483
+ video_result = search_pexels_video_tool(style, niche)
484
+ if video_result["success"]:
485
+ video_results.append(video_result)
486
+ status_log.append(f" βœ… Video {i+1}: {video_result['search_query']}")
487
+
488
+ if not video_results:
489
+ return "\n".join(status_log) + "\n❌ No videos found", None, []
490
 
491
+ status_log.append("")
 
492
 
493
+ # STEP 5: Create multiple video variations
494
+ status_log.append(f"🎬 **MCP TOOL: create_quote_video_tool (x{len(video_results)})**")
495
+ status_log.append(f" ⏳ Creating {len(video_results)} video variations...")
496
 
 
497
  output_dir = "/tmp/quote_videos"
498
  os.makedirs(output_dir, exist_ok=True)
499
 
500
+ created_videos = []
501
  import time
502
+ timestamp = int(time.time())
 
503
 
504
+ for i, video_result in enumerate(video_results):
505
+ output_filename = f"quote_video_v{i+1}_{timestamp}.mp4"
506
+ output_path = os.path.join(output_dir, output_filename)
507
+
508
+ creation_result = create_quote_video_tool(
509
+ video_result["video_url"],
510
+ quote,
511
+ output_path,
512
+ audio_path if add_voice else None
513
+ )
514
+
515
+ if creation_result["success"]:
516
+ created_videos.append(creation_result["output_path"])
517
+ status_log.append(f" βœ… Variation {i+1} created!")
518
+ else:
519
+ status_log.append(f" ⚠️ Variation {i+1} failed")
520
 
521
+ if not created_videos:
522
+ status_log.append("\n❌ All video creations failed")
523
+ return "\n".join(status_log), video_results[0]["video_url"] if video_results else None, []
 
 
524
 
525
+ status_log.append("")
526
 
527
+ # STEP 6: MCP Server integration status
528
  status_log.append("πŸ”— **MCP SERVER STATUS:**")
529
  if mcp_enabled:
530
  status_log.append(" βœ… Connected to: abidlabs-mcp-tools.hf.space")
 
531
  else:
532
  status_log.append(" ⚠️ MCP server connection pending")
533
  status_log.append("")
534
 
535
+ # STEP 7: Success!
536
  status_log.append("✨ **PIPELINE COMPLETE!**")
537
+ status_log.append(f" 🎬 Created {len(created_videos)} video variations")
538
+ if add_voice:
539
+ status_log.append(f" 🎀 With AI voice narration")
540
+ status_log.append(f" πŸ“₯ Choose your favorite and download!")
541
 
542
  final_status = "\n".join(status_log)
543
+ return final_status, video_results[0]["video_url"] if video_results else None, created_videos
544
 
545
  except Exception as e:
546
  status_log.append(f"\n❌ Pipeline error: {str(e)}")
547
+ return "\n".join(status_log), None, []
548
 
549
+ def fallback_pipeline(niche, style, num_variations=3, add_voice=True):
550
  """Fallback pipeline if MCP agent fails"""
551
  status_log = []
552
  status_log.append("πŸ”„ **FALLBACK MODE (Direct Tool Execution)**\n")
 
556
  quote = generate_quote_tool(niche, style)
557
 
558
  if "Error" in quote:
559
+ return "\n".join(status_log) + f"\n❌ {quote}", None, []
560
 
561
+ status_log.append(f" βœ… Quote generated\n")
562
 
563
+ # Generate voice if requested
564
+ audio_path = None
565
+ if add_voice:
566
+ status_log.append("🎀 Creating voice narration...")
567
+ audio_dir = "/tmp/quote_audio"
568
+ os.makedirs(audio_dir, exist_ok=True)
569
+
570
+ import time
571
+ audio_filename = f"narration_{int(time.time())}.mp3"
572
+ audio_path = os.path.join(audio_dir, audio_filename)
573
+
574
+ voice_result = generate_voice_narration_tool(quote, audio_path)
575
+ if voice_result["success"]:
576
+ status_log.append(f" βœ… Voice created\n")
577
+ else:
578
+ audio_path = None
579
+ status_log.append(f" ⚠️ Voice failed\n")
580
 
581
+ # Search videos
582
+ status_log.append(f"πŸ” Searching for {num_variations} videos...")
583
+ video_results = []
584
+ for i in range(num_variations):
585
+ video_result = search_pexels_video_tool(style, niche)
586
+ if video_result["success"]:
587
+ video_results.append(video_result)
588
+
589
+ if not video_results:
590
+ return "\n".join(status_log) + "\n❌ No videos found", None, []
591
 
592
+ status_log.append(f" βœ… Found {len(video_results)} videos\n")
593
 
594
+ # Create videos
595
+ status_log.append("🎬 Creating videos...")
596
  output_dir = "/tmp/quote_videos"
597
  os.makedirs(output_dir, exist_ok=True)
598
 
599
  import time
600
+ timestamp = int(time.time())
601
+ created_videos = []
602
 
603
+ for i, video_result in enumerate(video_results):
604
+ output_filename = f"quote_video_v{i+1}_{timestamp}.mp4"
605
+ output_path = os.path.join(output_dir, output_filename)
606
+
607
+ creation_result = create_quote_video_tool(
608
+ video_result["video_url"],
609
+ quote,
610
+ output_path,
611
+ audio_path if add_voice else None
612
+ )
613
+
614
+ if creation_result["success"]:
615
+ created_videos.append(creation_result["output_path"])
616
 
617
+ if not created_videos:
618
+ return "\n".join(status_log) + "\n❌ Video creation failed", video_results[0]["video_url"] if video_results else None, []
 
619
 
620
+ status_log.append(f" βœ… Created {len(created_videos)} videos!\n")
621
  status_log.append("🎬 **COMPLETE!**")
622
 
623
+ return "\n".join(status_log), video_results[0]["video_url"] if video_results else None, created_videos
624
 
625
  # Gradio Interface
626
  with gr.Blocks(title="AIQuoteClipGenerator - MCP Edition", theme=gr.themes.Soft()) as demo:
627
  gr.Markdown("""
628
  # 🎬 AIQuoteClipGenerator
629
+ ### MCP-Powered Autonomous AI Agent with Voice Narration
630
 
631
  **MCP Integration Features:**
632
  - πŸ”— **MCP Server:** Connected to smolagents framework
633
+ - πŸ› οΈ **4 Custom MCP Tools:** Quote generation + Video search + Voice narration + Video creation
634
  - πŸ€– **Agent Reasoning:** Autonomous task execution
635
  - ⚑ **Tool Orchestration:** Intelligent pipeline management
636
+ - 🎀 **ElevenLabs Voice:** AI narration for videos
637
+ - 🎨 **Multiple Variations:** Get 3 different video styles
638
  """)
639
 
640
  with gr.Row():
 
666
  value="Cinematic"
667
  )
668
 
669
+ num_variations = gr.Slider(
670
+ minimum=1,
671
+ maximum=5,
672
+ value=3,
673
+ step=1,
674
+ label="🎬 Number of Video Variations",
675
+ info="Generate multiple versions to choose from"
676
+ )
677
+
678
+ add_voice = gr.Checkbox(
679
+ value=True,
680
+ label="🎀 Add Voice Narration (ElevenLabs)",
681
+ info="AI voice will read the quote"
682
+ )
683
+
684
  generate_btn = gr.Button("πŸ€– Run MCP Agent", variant="primary", size="lg")
685
 
686
  with gr.Column():
 
692
  gr.Markdown("### πŸŽ₯ Background Video Preview")
693
  preview_video = gr.Video(label="Original Pexels Video")
694
 
695
+ with gr.Row():
696
+ gr.Markdown("### ✨ Your Quote Videos (Pick Your Favorite!)")
697
+
698
+ with gr.Row():
699
+ video_gallery = gr.Gallery(
700
+ label="Video Variations",
701
+ show_label=False,
702
+ elem_id="gallery",
703
+ columns=3,
704
+ rows=2,
705
+ height="auto",
706
+ object_fit="contain"
707
+ )
708
 
709
  gr.Markdown("""
710
  ---
711
+ ### ✨ NEW FEATURES!
712
+ - 🎀 **ElevenLabs Voice Narration** - AI voice reads your quotes
713
+ - 🎨 **Multiple Variations** - Get 3-5 different videos to choose from
714
+ - βœ… **4 MCP Tools** - Quote, Video Search, Voice, Video Creation
715
+
716
  ### ✨ MCP Implementation
717
  - βœ… **smolagents Framework** - Proper MCP integration
718
+ - βœ… **Custom MCP Tools** - 4 tools working autonomously
719
  - βœ… **CodeAgent** - Autonomous reasoning and execution
720
  - βœ… **MCP Client** - Connected to external MCP servers
721
+ - βœ… **MoviePy + PIL** - Professional text overlay
722
+ - βœ… **ElevenLabs** - AI voice narration
723
 
724
  ### πŸ† Hackathon: MCP 1st Birthday
725
  **Track:** Track 2 - MCP in Action
726
  **Category:** Productivity Tools
727
+ **Built with:** Gradio + smolagents + OpenAI + Pexels + ElevenLabs + MoviePy + MCP
728
  """)
729
 
730
  generate_btn.click(
731
  mcp_agent_pipeline,
732
+ inputs=[niche, style, num_variations, add_voice],
733
+ outputs=[output, preview_video, video_gallery]
734
  )
735
 
736
  if __name__ == "__main__":
requirements.txt CHANGED
@@ -9,4 +9,5 @@ imageio-ffmpeg
9
  decorator
10
  proglog
11
  numpy
12
- Pillow
 
 
9
  decorator
10
  proglog
11
  numpy
12
+ Pillow
13
+ elevenlabs