Spaces:
Running
Running
import gradio as gr | |
import os | |
import httpx | |
from typing import Dict, Any | |
# --- Backend Client Functions --- | |
# These functions call the Modal/backend endpoints. | |
async def call_video_analysis_backend(video_url: str) -> Dict[str, Any]: | |
"""Calls the backend to analyze a single video.""" | |
# Default to a placeholder if the env var is not set, to avoid crashing. | |
backend_url = os.getenv("BACKEND_VIDEO_URL", "https://your-backend-hf-space-for-video/process_video_analysis") | |
if not video_url: | |
return {"status": "error", "message": "Video URL cannot be empty."} | |
print(f"Sending request to backend for video: {video_url}") | |
payload = {"video_url": video_url} | |
try: | |
async with httpx.AsyncClient(timeout=1800.0) as client: | |
response = await client.post(backend_url, json=payload) | |
response.raise_for_status() | |
return response.json() | |
except httpx.HTTPStatusError as e: | |
return {"status": "error", "message": f"Backend Error: {e.response.status_code}", "details": e.response.text} | |
except Exception as e: | |
return {"status": "error", "message": "Failed to connect to backend", "details": str(e)} | |
async def call_topic_analysis_backend(topic: str, max_videos: int) -> Dict[str, Any]: | |
"""Calls the backend to analyze videos for a topic.""" | |
backend_url = os.getenv("BACKEND_TOPIC_URL", "https://your-backend-hf-space-for-topic/analyze_topic") | |
if not topic: | |
return {"status": "error", "message": "Topic cannot be empty."} | |
print(f"Sending request to backend for topic: {topic} ({max_videos} videos)") | |
payload = {"topic": topic, "max_videos": max_videos} | |
try: | |
async with httpx.AsyncClient(timeout=3600.0) as client: | |
response = await client.post(backend_url, json=payload) | |
response.raise_for_status() | |
return response.json() | |
except httpx.HTTPStatusError as e: | |
return {"status": "error", "message": f"Backend Error: {e.response.status_code}", "details": e.response.text} | |
except Exception as e: | |
return {"status": "error", "message": "Failed to connect to backend", "details": str(e)} | |
# --- Gradio Tool Functions (Wrappers for MCP) --- | |
async def analyze_video(video_url: str): | |
""" | |
Triggers a comprehensive analysis of a single video from a URL. | |
This tool calls a backend service to perform multiple analyses: | |
- Transcribes audio to text. | |
- Generates a descriptive caption for the video content. | |
- Recognizes main actions in the video. | |
- Detects objects in keyframes. | |
:param video_url: The public URL of the video to be processed (e.g., a YouTube link). | |
:return: A JSON object containing the full analysis results from the backend. | |
""" | |
status_update = f"Analyzing video: {video_url}..." | |
results = await call_video_analysis_backend(video_url) | |
if isinstance(results, dict) and results.get("analysis") is None: | |
status_update = f"Error analyzing video: {results.get('error', 'Unknown error')}" | |
else: | |
status_update = "Video analysis complete." | |
return status_update, results | |
async def analyze_topic(topic: str, max_videos: int): | |
""" | |
Finds and analyzes multiple videos based on a given topic. | |
This tool calls a backend service that searches for videos related to the topic, | |
then runs a comprehensive analysis on each video found. | |
:param topic: The topic to search for (e.g., 'latest AI advancements'). | |
:param max_videos: The maximum number of videos to find and analyze (1-5). | |
:return: A JSON object with the aggregated analysis results for all videos. | |
""" | |
status_update = f"Analyzing topic '{topic}' with {max_videos} videos... this can take a very long time." | |
results = await call_topic_analysis_backend(topic, max_videos) | |
if isinstance(results, dict) and results.get("results") is None: | |
status_update = f"Error analyzing topic: {results.get('error', 'Unknown error')}" | |
else: | |
status_update = "Topic analysis complete." | |
return status_update, results | |
# --- Gradio UI --- | |
with gr.Blocks(theme=gr.themes.Soft()) as demo: | |
gr.Markdown("# LLM Video Interpretation MCP") | |
gr.Markdown("This Hugging Face Space provides tools for processing video context for AI agents. Use the tools below to analyze videos by URL or by topic.") | |
with gr.Tab("Single Video Analysis"): | |
gr.Markdown("## Analyze a single video from a URL") | |
with gr.Row(): | |
video_url_input = gr.Textbox(label="Video URL", placeholder="Enter a YouTube or direct video URL...", scale=4) | |
submit_button = gr.Button("Analyze Video", variant="primary") | |
status_text = gr.Textbox(label="Status", interactive=False) | |
json_output = gr.JSON(label="Analysis Results") | |
submit_button.click( | |
analyze_video, | |
inputs=[video_url_input], | |
outputs=[status_text, json_output], | |
api_name="analyze_video" | |
) | |
gr.Examples( | |
examples=["https://www.youtube.com/watch?v=3wLg_t_H2Xw", "https://www.youtube.com/watch?v=h42dDpgE7g8"], | |
inputs=video_url_input | |
) | |
with gr.Tab("Topic Video Analysis"): | |
gr.Markdown("## Analyze multiple videos based on a topic") | |
with gr.Row(): | |
topic_input = gr.Textbox(label="Enter a topic", placeholder="e.g., 'Apple Vision Pro review'", scale=3) | |
max_videos_slider = gr.Slider(minimum=1, maximum=5, value=2, step=1, label="Number of Videos to Analyze") | |
topic_submit_button = gr.Button("Analyze Topic", variant="primary") | |
topic_status_text = gr.Textbox(label="Status", interactive=False) | |
topic_json_output = gr.JSON(label="Analysis Results") | |
topic_submit_button.click( | |
analyze_topic, | |
inputs=[topic_input, max_videos_slider], | |
outputs=[topic_status_text, topic_json_output], | |
api_name="analyze_topic" | |
) | |
gr.Examples( | |
examples=[["self-driving car technology", 2], ["open source large language models", 3]], | |
inputs=[topic_input, max_videos_slider] | |
) | |
# Set environment variables in your Hugging Face Space settings, not here. | |
# BACKEND_VIDEO_URL = "https://your-modal-or-backend-url/process_video_analysis" | |
# BACKEND_TOPIC_URL = "https://your-modal-or-backend-url/analyze_topic" | |
demo.launch() | |
gr.Markdown("**Processing can take several minutes** depending on video length and model inference times. The cache on the Modal backend will speed up repeated requests for the same video.") | |
with gr.Tab("Demo (for Manual Testing)"): | |
gr.Markdown("### Manually test video URLs or paths for interpretation and observe the JSON response.") | |
demo_interface.render() | |
with gr.Tab("Topic Video Analysis"): | |
gr.Markdown("### Analyze Multiple Videos Based on a Topic") | |
gr.Markdown("Enter a topic, and the system will search for relevant videos, analyze them, and provide an aggregated JSON output.") | |
with gr.Row(): | |
topic_input = gr.Textbox(label="Enter Topic", placeholder="e.g., 'best cat videos', 'Python programming tutorials'", scale=3) | |
max_videos_input = gr.Number(label="Max Videos to Analyze", value=3, minimum=1, maximum=5, step=1, scale=1) # Max 5 for UI, backend might support more | |
topic_analysis_output = gr.JSON(label="Topic Analysis Results") | |
with gr.Row(): | |
topic_submit_button = gr.Button("Analyze Topic Videos", variant="primary") | |
topic_clear_button = gr.Button("Clear") | |
topic_submit_button.click( | |
fn=call_topic_analysis_endpoint, | |
inputs=[topic_input, max_videos_input], | |
outputs=[topic_analysis_output] | |
) | |
def clear_topic_outputs(): | |
return [None, 3, None] # topic_input, max_videos_input (reset to default), topic_analysis_output | |
topic_clear_button.click(fn=clear_topic_outputs, inputs=[], outputs=[topic_input, max_videos_input, topic_analysis_output]) | |
gr.Examples( | |
examples=[ | |
["AI in healthcare", 2], | |
["sustainable energy solutions", 3], | |
["how to make sourdough bread", 1] | |
], | |
inputs=[topic_input, max_videos_input], | |
outputs=topic_analysis_output, | |
fn=call_topic_analysis_endpoint, | |
cache_examples=False | |
) | |
gr.Markdown("**Note:** This process involves searching for videos and then analyzing each one. It can take a significant amount of time, especially for multiple videos. The backend has a long timeout, but please be patient.") | |
# Launch the Gradio application | |
if __name__ == "__main__": | |
app.launch(debug=True, server_name="0.0.0.0") | |