update
Browse files- app.py +55 -92
- modal_video_processing.py +4 -4
app.py
CHANGED
|
@@ -603,107 +603,70 @@ def mcp_agent_pipeline(niche, style, num_variations=1):
|
|
| 603 |
|
| 604 |
# STEP 4: Create multiple video variations
|
| 605 |
status_log.append(f"🎬 **MCP TOOL: create_quote_video_tool (x{len(video_results)})**")
|
| 606 |
-
status_log.append(f" ⏳ Creating {len(video_results)} video variations...")
|
| 607 |
|
| 608 |
output_dir = "/tmp/quote_videos"
|
| 609 |
-
gallery_dir = "/data/gallery_videos"
|
| 610 |
os.makedirs(output_dir, exist_ok=True)
|
| 611 |
os.makedirs(gallery_dir, exist_ok=True)
|
| 612 |
|
| 613 |
-
created_videos = []
|
| 614 |
import time
|
| 615 |
timestamp = int(time.time())
|
| 616 |
|
| 617 |
-
# Use
|
| 618 |
-
|
| 619 |
-
|
| 620 |
-
|
| 621 |
-
|
| 622 |
-
|
| 623 |
-
|
| 624 |
-
|
| 625 |
-
|
| 626 |
-
|
| 627 |
-
|
| 628 |
-
|
| 629 |
-
|
| 630 |
-
|
| 631 |
-
|
| 632 |
-
|
| 633 |
-
|
| 634 |
-
|
| 635 |
-
|
| 636 |
-
|
| 637 |
-
|
| 638 |
-
|
| 639 |
-
|
| 640 |
-
|
| 641 |
-
|
| 642 |
-
|
| 643 |
-
|
| 644 |
-
|
| 645 |
-
|
| 646 |
-
|
| 647 |
-
|
| 648 |
-
|
| 649 |
-
|
| 650 |
-
|
| 651 |
-
|
| 652 |
-
|
| 653 |
-
|
| 654 |
-
|
| 655 |
-
|
| 656 |
-
|
| 657 |
-
|
| 658 |
-
|
| 659 |
-
|
| 660 |
-
|
| 661 |
-
gallery_path = os.path.join(gallery_dir, gallery_filename)
|
| 662 |
-
try:
|
| 663 |
-
shutil.copy2(output_path, gallery_path)
|
| 664 |
-
except:
|
| 665 |
-
pass
|
| 666 |
-
|
| 667 |
-
status_log.append(f" ⚡ Batch processing complete! All {len(created_videos)} videos done in parallel")
|
| 668 |
-
else:
|
| 669 |
-
status_log.append(f" ⚠️ Batch failed, falling back to sequential...")
|
| 670 |
-
raise Exception("Batch failed")
|
| 671 |
-
else:
|
| 672 |
-
status_log.append(f" ⚠️ Batch endpoint error, falling back to sequential...")
|
| 673 |
-
raise Exception("Batch endpoint error")
|
| 674 |
-
|
| 675 |
-
except Exception as e:
|
| 676 |
-
# Fall back to sequential processing
|
| 677 |
-
status_log.append(f" ⚠️ Batch processing failed: {e}")
|
| 678 |
-
status_log.append(f" 🔄 Falling back to sequential processing...")
|
| 679 |
-
created_videos = [] # Reset
|
| 680 |
-
|
| 681 |
-
# Sequential processing (fallback or single video)
|
| 682 |
-
if len(created_videos) == 0:
|
| 683 |
-
for i, video_result in enumerate(video_results):
|
| 684 |
-
output_filename = f"quote_video_v{i+1}_{timestamp}.mp4"
|
| 685 |
-
output_path = os.path.join(output_dir, output_filename)
|
| 686 |
-
|
| 687 |
-
creation_result = create_quote_video_tool(
|
| 688 |
-
video_result["video_url"],
|
| 689 |
-
quote,
|
| 690 |
-
output_path,
|
| 691 |
-
None # No audio
|
| 692 |
-
)
|
| 693 |
|
| 694 |
-
|
| 695 |
-
|
| 696 |
-
|
| 697 |
-
|
| 698 |
-
|
| 699 |
-
|
| 700 |
-
|
| 701 |
-
|
| 702 |
-
|
| 703 |
-
shutil.copy2(creation_result["output_path"], gallery_path)
|
| 704 |
-
except:
|
| 705 |
-
pass # Silently fail if can't copy to gallery
|
| 706 |
-
else:
|
| 707 |
error_msg = creation_result.get("message", "Unknown error")
|
| 708 |
status_log.append(f" ⚠️ Variation {i+1} failed: {error_msg}")
|
| 709 |
|
|
|
|
| 603 |
|
| 604 |
# STEP 4: Create multiple video variations
|
| 605 |
status_log.append(f"🎬 **MCP TOOL: create_quote_video_tool (x{len(video_results)})**")
|
| 606 |
+
status_log.append(f" ⏳ Creating {len(video_results)} video variations in parallel...")
|
| 607 |
|
| 608 |
output_dir = "/tmp/quote_videos"
|
| 609 |
+
gallery_dir = "/data/gallery_videos"
|
| 610 |
os.makedirs(output_dir, exist_ok=True)
|
| 611 |
os.makedirs(gallery_dir, exist_ok=True)
|
| 612 |
|
|
|
|
| 613 |
import time
|
| 614 |
timestamp = int(time.time())
|
| 615 |
|
| 616 |
+
# Use threading for parallel Modal calls
|
| 617 |
+
import threading
|
| 618 |
+
import queue
|
| 619 |
+
|
| 620 |
+
results_queue = queue.Queue()
|
| 621 |
+
|
| 622 |
+
def create_single_video(index, video_result):
|
| 623 |
+
output_filename = f"quote_video_v{index+1}_{timestamp}.mp4"
|
| 624 |
+
output_path = os.path.join(output_dir, output_filename)
|
| 625 |
+
|
| 626 |
+
creation_result = create_quote_video_tool(
|
| 627 |
+
video_result["video_url"],
|
| 628 |
+
quote,
|
| 629 |
+
output_path,
|
| 630 |
+
None
|
| 631 |
+
)
|
| 632 |
+
|
| 633 |
+
results_queue.put((index, creation_result, output_path))
|
| 634 |
+
|
| 635 |
+
# Start all threads
|
| 636 |
+
threads = []
|
| 637 |
+
for i, video_result in enumerate(video_results):
|
| 638 |
+
thread = threading.Thread(target=create_single_video, args=(i, video_result))
|
| 639 |
+
thread.start()
|
| 640 |
+
threads.append(thread)
|
| 641 |
+
|
| 642 |
+
# Wait for all to complete
|
| 643 |
+
for thread in threads:
|
| 644 |
+
thread.join()
|
| 645 |
+
|
| 646 |
+
# Collect results
|
| 647 |
+
created_videos = []
|
| 648 |
+
all_results = []
|
| 649 |
+
while not results_queue.empty():
|
| 650 |
+
all_results.append(results_queue.get())
|
| 651 |
+
|
| 652 |
+
# Sort by index
|
| 653 |
+
all_results.sort(key=lambda x: x[0])
|
| 654 |
+
|
| 655 |
+
# Process results
|
| 656 |
+
for index, creation_result, output_path in all_results:
|
| 657 |
+
if creation_result["success"]:
|
| 658 |
+
created_videos.append(output_path)
|
| 659 |
+
status_log.append(f" ✅ Variation {index+1} created!")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 660 |
|
| 661 |
+
# Copy to gallery
|
| 662 |
+
import shutil
|
| 663 |
+
gallery_filename = f"gallery_{timestamp}_v{index+1}.mp4"
|
| 664 |
+
gallery_path = os.path.join(gallery_dir, gallery_filename)
|
| 665 |
+
try:
|
| 666 |
+
shutil.copy2(output_path, gallery_path)
|
| 667 |
+
except:
|
| 668 |
+
pass
|
| 669 |
+
else:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 670 |
error_msg = creation_result.get("message", "Unknown error")
|
| 671 |
status_log.append(f" ⚠️ Variation {i+1} failed: {error_msg}")
|
| 672 |
|
modal_video_processing.py
CHANGED
|
@@ -23,9 +23,9 @@ image = modal.Image.debian_slim(python_version="3.11").pip_install(
|
|
| 23 |
cpu=2,
|
| 24 |
memory=2048,
|
| 25 |
timeout=180,
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
)
|
| 30 |
def process_quote_video(video_url: str, quote_text: str, audio_b64: str = None) -> bytes:
|
| 31 |
"""
|
|
@@ -215,4 +215,4 @@ def process_batch_endpoint(data: dict):
|
|
| 215 |
|
| 216 |
except Exception as e:
|
| 217 |
return {"error": str(e)}, 500
|
| 218 |
-
|
|
|
|
| 23 |
cpu=2,
|
| 24 |
memory=2048,
|
| 25 |
timeout=180,
|
| 26 |
+
concurrency_limit=10, # Allow 10 videos at once
|
| 27 |
+
allow_concurrent_inputs=10, # Process multiple in parallel
|
| 28 |
+
container_idle_timeout=120,
|
| 29 |
)
|
| 30 |
def process_quote_video(video_url: str, quote_text: str, audio_b64: str = None) -> bytes:
|
| 31 |
"""
|
|
|
|
| 215 |
|
| 216 |
except Exception as e:
|
| 217 |
return {"error": str(e)}, 500
|
| 218 |
+
|