Spaces:
Runtime error
Runtime error
| import gradio as gr | |
| import uuid | |
| import subprocess | |
| import threading | |
| import os | |
| import time | |
| from fastapi import FastAPI | |
| from fastapi.responses import FileResponse | |
| import asyncio | |
| # A simple in-memory dictionary to track task status. | |
| # For a production system, you'd use a database or Redis. | |
| tasks = {} | |
| def run_video_generation(task_id: str, topic: str, context: str): | |
| """ | |
| This function runs the main generation script in a separate process. | |
| """ | |
| tasks[task_id]['status'] = 'running' | |
| # Sanitize topic to create a valid directory name | |
| file_prefix = "".join(c if c.isalnum() else "_" for c in topic.lower()) | |
| output_dir = os.path.join("output", file_prefix) | |
| command = [ | |
| "python", "generate_video.py", | |
| "--model", "openai/o3-mini", # Or get from request | |
| "--topic", topic, | |
| "--context", context, | |
| "--output_dir", "output", | |
| "--use_langfuse" # Assuming you have secrets set | |
| ] | |
| try: | |
| # Using subprocess to run the existing script | |
| process = subprocess.run(command, check=True, capture_output=True, text=True) | |
| # Assume the final video is named based on the topic | |
| # Note: The actual video path might differ. This is an assumption. | |
| # You may need to parse the stdout from generate_video.py to get the exact path. | |
| video_path = None | |
| for file in os.listdir(output_dir): | |
| if file.endswith("_combined.mp4"): | |
| video_path = os.path.join(output_dir, file) | |
| break | |
| if video_path and os.path.exists(video_path): | |
| tasks[task_id]['status'] = 'completed' | |
| tasks[task_id]['video_path'] = video_path | |
| else: | |
| tasks[task_id]['status'] = 'failed' | |
| tasks[task_id]['error'] = "Video file not found after generation." | |
| tasks[task_id]['stdout'] = process.stdout | |
| tasks[task_id]['stderr'] = process.stderr | |
| except subprocess.CalledProcessError as e: | |
| tasks[task_id]['status'] = 'failed' | |
| tasks[task_id]['error'] = str(e) | |
| tasks[task_id]['stdout'] = e.stdout | |
| tasks[task_id]['stderr'] = e.stderr | |
| except Exception as e: | |
| tasks[task_id]['status'] = 'failed' | |
| tasks[task_id]['error'] = str(e) | |
| def start_generation_thread(topic: str, context: str): | |
| if not topic or not context: | |
| return "Topic and Context cannot be empty.", "", None | |
| task_id = str(uuid.uuid4()) | |
| tasks[task_id] = {'status': 'queued'} | |
| # Use a background thread to run the time-consuming task | |
| thread = threading.Thread( | |
| target=run_video_generation, | |
| args=(task_id, topic, context) | |
| ) | |
| thread.start() | |
| return f"Task started. Your Task ID is: {task_id}", task_id, None | |
| def check_status(task_id: str): | |
| if not task_id: | |
| return "Please provide a Task ID.", None | |
| task = tasks.get(task_id) | |
| if not task: | |
| return "Task not found.", None | |
| status = task.get('status') | |
| if status == 'completed': | |
| video_path = task.get('video_path') | |
| return f"Status: {status}", video_path | |
| elif status == 'failed': | |
| error = task.get('error', 'Unknown error') | |
| stdout = task.get('stdout', '') | |
| stderr = task.get('stderr', '') | |
| return f"Status: {status}\nError: {error}\nOutput: {stdout}\nStderr: {stderr}", None | |
| return f"Status: {status}", None | |
| # We need a lightweight FastAPI app in the background to serve the video files. | |
| # Gradio can't serve files directly from arbitrary paths in a secure way. | |
| fastapi_app = FastAPI() | |
| def get_video(task_id: str): | |
| """ | |
| Serves the final generated video file. | |
| """ | |
| task = tasks.get(task_id) | |
| if not task or task.get('status') != 'completed': | |
| return {"error": "Task not completed or not found"} | |
| video_path = task.get('video_path') | |
| if not os.path.exists(video_path): | |
| return {"error": "Video file not found."} | |
| return FileResponse(video_path, media_type="video/mp4", filename=os.path.basename(video_path)) | |
| # Gradio Interface | |
| with gr.Blocks() as demo: | |
| gr.Markdown("# Theorem-Explain-Agent Video Generation") | |
| gr.Markdown("Start a video generation task and check its status.") | |
| with gr.Tab("Start Generation"): | |
| topic_input = gr.Textbox(label="Topic", placeholder="e.g., The Pythagorean Theorem") | |
| context_input = gr.Textbox(label="Context", placeholder="A short explanation of the theorem.") | |
| start_button = gr.Button("Generate Video") | |
| with gr.Column(): | |
| task_id_output = gr.Textbox(label="Task ID", interactive=False) | |
| status_output_start = gr.Textbox(label="Status", interactive=False) | |
| with gr.Tab("Check Status"): | |
| task_id_input = gr.Textbox(label="Task ID", placeholder="Enter the Task ID you received.") | |
| check_button = gr.Button("Check Status") | |
| with gr.Column(): | |
| status_output_check = gr.Textbox(label="Status", interactive=False) | |
| video_output = gr.Video(label="Generated Video") | |
| # Actions | |
| start_button.click( | |
| fn=start_generation_thread, | |
| inputs=[topic_input, context_input], | |
| outputs=[status_output_start, task_id_output, video_output] # Clear video on new task | |
| ) | |
| check_button.click( | |
| fn=check_status, | |
| inputs=[task_id_input], | |
| outputs=[status_output_check, video_output] | |
| ) | |
| gr.Markdown("### How to Use") | |
| gr.Markdown( | |
| "1. Enter a `Topic` and `Context` in the 'Start Generation' tab and click 'Generate Video'.\n" | |
| "2. Copy the `Task ID` that appears.\n" | |
| "3. Go to the 'Check Status' tab, paste the `Task ID`, and click 'Check Status' periodically.\n" | |
| "4. When the generation is complete, the video will appear." | |
| ) | |
| # To run both Gradio and FastAPI, we mount the FastAPI app into Gradio's internal FastAPI app. | |
| app = gr.mount_ όπου(demo, fastapi_app, path="/") |