Spaces:
Sleeping
Sleeping
Miquel Farre
commited on
Commit
·
02abdab
1
Parent(s):
0fe0281
initial test
Browse files
app.py
CHANGED
|
@@ -3,10 +3,12 @@ import json
|
|
| 3 |
import gradio as gr
|
| 4 |
import tempfile
|
| 5 |
from PIL import Image, ImageDraw, ImageFont
|
|
|
|
| 6 |
from typing import Tuple, Optional
|
|
|
|
| 7 |
from pathlib import Path
|
|
|
|
| 8 |
|
| 9 |
-
# Import your highlight detection code
|
| 10 |
from video_highlight_detector import (
|
| 11 |
load_model,
|
| 12 |
BatchedVideoHighlightDetector,
|
|
@@ -14,12 +16,10 @@ from video_highlight_detector import (
|
|
| 14 |
)
|
| 15 |
|
| 16 |
def load_examples(json_path: str) -> dict:
|
| 17 |
-
"""Load pre-computed examples from JSON file"""
|
| 18 |
with open(json_path, 'r') as f:
|
| 19 |
return json.load(f)
|
| 20 |
|
| 21 |
def format_duration(seconds: int) -> str:
|
| 22 |
-
"""Convert seconds to MM:SS or HH:MM:SS format"""
|
| 23 |
hours = seconds // 3600
|
| 24 |
minutes = (seconds % 3600) // 60
|
| 25 |
secs = seconds % 60
|
|
@@ -28,7 +28,6 @@ def format_duration(seconds: int) -> str:
|
|
| 28 |
return f"{minutes}:{secs:02d}"
|
| 29 |
|
| 30 |
def add_watermark(video_path: str, output_path: str):
|
| 31 |
-
"""Add watermark to video using ffmpeg"""
|
| 32 |
watermark_text = "🤗 SmolVLM2 Highlight"
|
| 33 |
command = f"""ffmpeg -i {video_path} -vf \
|
| 34 |
"drawtext=text='{watermark_text}':fontcolor=white:fontsize=24:box=1:[email protected]:\
|
|
@@ -40,48 +39,33 @@ def process_video(
|
|
| 40 |
video_path: str,
|
| 41 |
progress = gr.Progress()
|
| 42 |
) -> Tuple[str, str, str, str]:
|
| 43 |
-
"""
|
| 44 |
-
Process video and return paths to:
|
| 45 |
-
- Processed video with watermark
|
| 46 |
-
- Video description
|
| 47 |
-
- Highlight types
|
| 48 |
-
- Error message (if any)
|
| 49 |
-
"""
|
| 50 |
try:
|
| 51 |
-
# Check video duration
|
| 52 |
duration = get_video_duration_seconds(video_path)
|
| 53 |
if duration > 1200: # 20 minutes
|
| 54 |
return None, None, None, "Video must be shorter than 20 minutes"
|
| 55 |
|
| 56 |
-
# Load model (could be cached)
|
| 57 |
progress(0.1, desc="Loading model...")
|
| 58 |
model, processor = load_model()
|
| 59 |
detector = BatchedVideoHighlightDetector(model, processor)
|
| 60 |
|
| 61 |
-
# Analyze video content
|
| 62 |
progress(0.2, desc="Analyzing video content...")
|
| 63 |
video_description = detector.analyze_video_content(video_path)
|
| 64 |
|
| 65 |
-
# Determine highlights
|
| 66 |
progress(0.3, desc="Determining highlight types...")
|
| 67 |
highlight_types = detector.determine_highlights(video_description)
|
| 68 |
|
| 69 |
-
# Create highlight video
|
| 70 |
progress(0.4, desc="Detecting and extracting highlights...")
|
| 71 |
with tempfile.NamedTemporaryFile(suffix='.mp4', delete=False) as tmp_file:
|
| 72 |
temp_output = tmp_file.name
|
| 73 |
|
| 74 |
detector.create_highlight_video(video_path, temp_output)
|
| 75 |
|
| 76 |
-
# Add watermark
|
| 77 |
progress(0.9, desc="Adding watermark...")
|
| 78 |
output_path = temp_output.replace('.mp4', '_watermark.mp4')
|
| 79 |
add_watermark(temp_output, output_path)
|
| 80 |
|
| 81 |
-
# Cleanup
|
| 82 |
os.unlink(temp_output)
|
| 83 |
|
| 84 |
-
# Truncate description and highlights if too long
|
| 85 |
video_description = video_description[:500] + "..." if len(video_description) > 500 else video_description
|
| 86 |
highlight_types = highlight_types[:500] + "..." if len(highlight_types) > 500 else highlight_types
|
| 87 |
|
|
@@ -90,24 +74,20 @@ def process_video(
|
|
| 90 |
except Exception as e:
|
| 91 |
return None, None, None, f"Error processing video: {str(e)}"
|
| 92 |
|
| 93 |
-
|
| 94 |
def create_ui(examples_path: str):
|
| 95 |
-
"""Create the Gradio interface with optional thumbnails"""
|
| 96 |
examples_data = load_examples(examples_path)
|
| 97 |
|
| 98 |
with gr.Blocks() as app:
|
| 99 |
gr.Markdown("# Video Highlight Generator")
|
| 100 |
gr.Markdown("Upload a video (max 20 minutes) and get an automated highlight reel!")
|
| 101 |
|
| 102 |
-
# Pre-computed examples section
|
| 103 |
with gr.Row():
|
| 104 |
gr.Markdown("## Example Results")
|
| 105 |
|
| 106 |
for example in examples_data["examples"]:
|
| 107 |
with gr.Row():
|
| 108 |
with gr.Column():
|
| 109 |
-
|
| 110 |
-
video_component = gr.Video(
|
| 111 |
example["original"]["url"],
|
| 112 |
label=f"Original ({format_duration(example['original']['duration_seconds'])})",
|
| 113 |
)
|
|
@@ -122,15 +102,13 @@ def create_ui(examples_path: str):
|
|
| 122 |
gr.Markdown(example["analysis"]["video_description"])
|
| 123 |
gr.Markdown(example["analysis"]["highlight_types"])
|
| 124 |
|
| 125 |
-
# Upload section
|
| 126 |
gr.Markdown("## Try It Yourself!")
|
| 127 |
with gr.Row():
|
| 128 |
input_video = gr.Video(
|
| 129 |
label="Upload your video (max 20 minutes)",
|
| 130 |
-
|
| 131 |
)
|
| 132 |
|
| 133 |
-
# Results section (initially hidden)
|
| 134 |
with gr.Row(visible=False) as results_row:
|
| 135 |
with gr.Column():
|
| 136 |
video_description = gr.Markdown(label="Video Analysis")
|
|
@@ -141,10 +119,8 @@ def create_ui(examples_path: str):
|
|
| 141 |
output_video = gr.Video(label="Highlight Video")
|
| 142 |
download_btn = gr.Button("Download Highlights")
|
| 143 |
|
| 144 |
-
# Error message
|
| 145 |
error_msg = gr.Markdown(visible=False)
|
| 146 |
|
| 147 |
-
# Process video when uploaded
|
| 148 |
def on_upload(video):
|
| 149 |
results_row.visible = False
|
| 150 |
output_row.visible = False
|
|
@@ -172,7 +148,6 @@ def create_ui(examples_path: str):
|
|
| 172 |
outputs=[output_video, video_description, highlight_types, error_msg]
|
| 173 |
)
|
| 174 |
|
| 175 |
-
# Download button
|
| 176 |
download_btn.click(
|
| 177 |
lambda x: x,
|
| 178 |
inputs=[output_video],
|
|
@@ -183,4 +158,4 @@ def create_ui(examples_path: str):
|
|
| 183 |
|
| 184 |
if __name__ == "__main__":
|
| 185 |
app = create_ui("video_spec.json")
|
| 186 |
-
app.launch()
|
|
|
|
| 3 |
import gradio as gr
|
| 4 |
import tempfile
|
| 5 |
from PIL import Image, ImageDraw, ImageFont
|
| 6 |
+
import cv2
|
| 7 |
from typing import Tuple, Optional
|
| 8 |
+
import torch
|
| 9 |
from pathlib import Path
|
| 10 |
+
import time
|
| 11 |
|
|
|
|
| 12 |
from video_highlight_detector import (
|
| 13 |
load_model,
|
| 14 |
BatchedVideoHighlightDetector,
|
|
|
|
| 16 |
)
|
| 17 |
|
| 18 |
def load_examples(json_path: str) -> dict:
|
|
|
|
| 19 |
with open(json_path, 'r') as f:
|
| 20 |
return json.load(f)
|
| 21 |
|
| 22 |
def format_duration(seconds: int) -> str:
|
|
|
|
| 23 |
hours = seconds // 3600
|
| 24 |
minutes = (seconds % 3600) // 60
|
| 25 |
secs = seconds % 60
|
|
|
|
| 28 |
return f"{minutes}:{secs:02d}"
|
| 29 |
|
| 30 |
def add_watermark(video_path: str, output_path: str):
|
|
|
|
| 31 |
watermark_text = "🤗 SmolVLM2 Highlight"
|
| 32 |
command = f"""ffmpeg -i {video_path} -vf \
|
| 33 |
"drawtext=text='{watermark_text}':fontcolor=white:fontsize=24:box=1:[email protected]:\
|
|
|
|
| 39 |
video_path: str,
|
| 40 |
progress = gr.Progress()
|
| 41 |
) -> Tuple[str, str, str, str]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
try:
|
|
|
|
| 43 |
duration = get_video_duration_seconds(video_path)
|
| 44 |
if duration > 1200: # 20 minutes
|
| 45 |
return None, None, None, "Video must be shorter than 20 minutes"
|
| 46 |
|
|
|
|
| 47 |
progress(0.1, desc="Loading model...")
|
| 48 |
model, processor = load_model()
|
| 49 |
detector = BatchedVideoHighlightDetector(model, processor)
|
| 50 |
|
|
|
|
| 51 |
progress(0.2, desc="Analyzing video content...")
|
| 52 |
video_description = detector.analyze_video_content(video_path)
|
| 53 |
|
|
|
|
| 54 |
progress(0.3, desc="Determining highlight types...")
|
| 55 |
highlight_types = detector.determine_highlights(video_description)
|
| 56 |
|
|
|
|
| 57 |
progress(0.4, desc="Detecting and extracting highlights...")
|
| 58 |
with tempfile.NamedTemporaryFile(suffix='.mp4', delete=False) as tmp_file:
|
| 59 |
temp_output = tmp_file.name
|
| 60 |
|
| 61 |
detector.create_highlight_video(video_path, temp_output)
|
| 62 |
|
|
|
|
| 63 |
progress(0.9, desc="Adding watermark...")
|
| 64 |
output_path = temp_output.replace('.mp4', '_watermark.mp4')
|
| 65 |
add_watermark(temp_output, output_path)
|
| 66 |
|
|
|
|
| 67 |
os.unlink(temp_output)
|
| 68 |
|
|
|
|
| 69 |
video_description = video_description[:500] + "..." if len(video_description) > 500 else video_description
|
| 70 |
highlight_types = highlight_types[:500] + "..." if len(highlight_types) > 500 else highlight_types
|
| 71 |
|
|
|
|
| 74 |
except Exception as e:
|
| 75 |
return None, None, None, f"Error processing video: {str(e)}"
|
| 76 |
|
|
|
|
| 77 |
def create_ui(examples_path: str):
|
|
|
|
| 78 |
examples_data = load_examples(examples_path)
|
| 79 |
|
| 80 |
with gr.Blocks() as app:
|
| 81 |
gr.Markdown("# Video Highlight Generator")
|
| 82 |
gr.Markdown("Upload a video (max 20 minutes) and get an automated highlight reel!")
|
| 83 |
|
|
|
|
| 84 |
with gr.Row():
|
| 85 |
gr.Markdown("## Example Results")
|
| 86 |
|
| 87 |
for example in examples_data["examples"]:
|
| 88 |
with gr.Row():
|
| 89 |
with gr.Column():
|
| 90 |
+
gr.Video(
|
|
|
|
| 91 |
example["original"]["url"],
|
| 92 |
label=f"Original ({format_duration(example['original']['duration_seconds'])})",
|
| 93 |
)
|
|
|
|
| 102 |
gr.Markdown(example["analysis"]["video_description"])
|
| 103 |
gr.Markdown(example["analysis"]["highlight_types"])
|
| 104 |
|
|
|
|
| 105 |
gr.Markdown("## Try It Yourself!")
|
| 106 |
with gr.Row():
|
| 107 |
input_video = gr.Video(
|
| 108 |
label="Upload your video (max 20 minutes)",
|
| 109 |
+
interactive=True
|
| 110 |
)
|
| 111 |
|
|
|
|
| 112 |
with gr.Row(visible=False) as results_row:
|
| 113 |
with gr.Column():
|
| 114 |
video_description = gr.Markdown(label="Video Analysis")
|
|
|
|
| 119 |
output_video = gr.Video(label="Highlight Video")
|
| 120 |
download_btn = gr.Button("Download Highlights")
|
| 121 |
|
|
|
|
| 122 |
error_msg = gr.Markdown(visible=False)
|
| 123 |
|
|
|
|
| 124 |
def on_upload(video):
|
| 125 |
results_row.visible = False
|
| 126 |
output_row.visible = False
|
|
|
|
| 148 |
outputs=[output_video, video_description, highlight_types, error_msg]
|
| 149 |
)
|
| 150 |
|
|
|
|
| 151 |
download_btn.click(
|
| 152 |
lambda x: x,
|
| 153 |
inputs=[output_video],
|
|
|
|
| 158 |
|
| 159 |
if __name__ == "__main__":
|
| 160 |
app = create_ui("video_spec.json")
|
| 161 |
+
app.launch()
|