import gradio as gr
import shutil
import os
from dotenv import load_dotenv
load_dotenv()
from modules.summarizer import summarize_text
from modules.frame_extractor import extract_frame
from modules.thumbnail import generate_thumbnail
from modules.composer import compose_videos
from modules.subtitles import generate_subtitles
from modules.subtitle_burner import burn_subtitles
UPLOAD_DIR = "assets/uploads"
os.makedirs(UPLOAD_DIR, exist_ok=True)
# ---------------- FUNCTIONS ----------------
def save_uploaded_video(video_file):
if video_file is None:
return "❌ No video uploaded."
filename = os.path.basename(video_file)
destination = os.path.join(UPLOAD_DIR, filename)
shutil.copy(video_file, destination)
return f"✅ Video uploaded: {filename}"
def summarize_ui(text):
if not text.strip():
return "Please enter text."
return summarize_text(text)
def create_thumbnail(video_file, title_text):
if video_file is None:
return None
frame = extract_frame(video_file)
return generate_thumbnail(frame, title_text)
def compose_ui(video_files):
if not video_files:
return None
return compose_videos(video_files)
def subtitle_ui(video):
if video is None:
return None
return generate_subtitles(video)
def burn_subtitles_ui(video, subtitle_file):
if video is None or subtitle_file is None:
return None
return burn_subtitles(video, subtitle_file)
# ---------------- CSS ----------------
custom_css = """
body{
background:linear-gradient(135deg,#020617,#0f172a);
font-family:Inter;
}
.hero{
text-align:center;
padding:50px;
}
.hero-title{
font-size:44px;
font-weight:800;
background:linear-gradient(90deg,#60a5fa,#a78bfa,#c084fc);
-webkit-background-clip:text;
-webkit-text-fill-color:transparent;
}
.navbar{
display:flex;
gap:20px;
justify-content:center;
margin-bottom:25px;
}
.card{
background:#0f172a;
padding:25px;
border-radius:14px;
box-shadow:0 8px 20px rgba(0,0,0,0.4);
}
"""
# ---------------- UI ----------------
with gr.Blocks(css=custom_css, theme=gr.themes.Soft()) as app:
with gr.Column(elem_classes="hero"):
gr.Markdown("""
🎬 ClipForge AI
Create viral short-form content using AI
""")
# NAVIGATION BAR
with gr.Row():
btn_upload = gr.Button("📥 Upload")
btn_summary = gr.Button("📝 Summary")
btn_thumbnail = gr.Button("🖼 Thumbnail")
btn_subtitle = gr.Button("🎤 Subtitles")
btn_composer = gr.Button("🎞 Composer")
btn_burn = gr.Button("🔥 Burn")
# SECTIONS
with gr.Column(visible=True) as upload_section:
with gr.Column(elem_classes="card"):
video_input = gr.Video()
upload_btn = gr.Button("Upload Video")
upload_status = gr.Textbox(label="Status")
upload_btn.click(
save_uploaded_video,
inputs=video_input,
outputs=upload_status
)
with gr.Column(visible=False) as summary_section:
with gr.Column(elem_classes="card"):
input_text = gr.Textbox(lines=5)
summarize_btn = gr.Button("Generate Summary")
output_text = gr.Textbox()
summarize_btn.click(
summarize_ui,
inputs=input_text,
outputs=output_text
)
with gr.Column(visible=False) as thumb_section:
with gr.Column(elem_classes="card"):
video_for_thumb = gr.Video()
title_text = gr.Textbox()
thumb_btn = gr.Button("Generate Thumbnail")
thumbnail_output = gr.Image()
thumb_btn.click(
create_thumbnail,
inputs=[video_for_thumb, title_text],
outputs=thumbnail_output
)
with gr.Column(visible=False) as subtitle_section:
with gr.Column(elem_classes="card"):
video_input_sub = gr.Video()
subtitle_btn = gr.Button("Generate Subtitles")
subtitle_file = gr.File()
subtitle_btn.click(
subtitle_ui,
inputs=video_input_sub,
outputs=subtitle_file
)
with gr.Column(visible=False) as composer_section:
with gr.Column(elem_classes="card"):
video_inputs = gr.File(file_types=[".mp4"], file_count="multiple")
compose_btn = gr.Button("Compose Video")
final_video = gr.Video()
compose_btn.click(
compose_ui,
inputs=video_inputs,
outputs=final_video
)
with gr.Column(visible=False) as burn_section:
with gr.Column(elem_classes="card"):
video_input_burn = gr.Video()
subtitle_input = gr.File()
burn_btn = gr.Button("Burn Subtitles")
output_video = gr.Video()
burn_btn.click(
burn_subtitles_ui,
inputs=[video_input_burn, subtitle_input],
outputs=output_video
)
# ---------- NAVIGATION FUNCTIONS (FIXED) ----------
def show_upload():
return (
gr.update(visible=True),
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=False),
)
def show_summary():
return (
gr.update(visible=False),
gr.update(visible=True),
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=False),
)
def show_thumb():
return (
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=True),
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=False),
)
def show_subtitle():
return (
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=True),
gr.update(visible=False),
gr.update(visible=False),
)
def show_composer():
return (
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=True),
gr.update(visible=False),
)
def show_burn():
return (
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=True),
)
btn_upload.click(show_upload,
outputs=[upload_section, summary_section, thumb_section, subtitle_section, composer_section, burn_section])
btn_summary.click(show_summary,
outputs=[upload_section, summary_section, thumb_section, subtitle_section, composer_section, burn_section])
btn_thumbnail.click(show_thumb,
outputs=[upload_section, summary_section, thumb_section, subtitle_section, composer_section, burn_section])
btn_subtitle.click(show_subtitle,
outputs=[upload_section, summary_section, thumb_section, subtitle_section, composer_section, burn_section])
btn_composer.click(show_composer,
outputs=[upload_section, summary_section, thumb_section, subtitle_section, composer_section, burn_section])
btn_burn.click(show_burn,
outputs=[upload_section, summary_section, thumb_section, subtitle_section, composer_section, burn_section])
app.launch(server_name="0.0.0.0", server_port=7860)