Spaces:
Sleeping
Sleeping
# Import necessary libraries (assuming all your imports remain the same) | |
import gradio as gr | |
import os | |
import tempfile | |
import shutil | |
from moviepy.editor import concatenate_videoclips, CompositeVideoClip, AudioFileClip, TextClip | |
# Your existing helper functions (generate_script, parse_script, etc.) remain unchanged | |
# Ensure TEMP_FOLDER, TARGET_RESOLUTION, and CAPTION_COLOR are set within functions as needed | |
# Define maximum number of clips to handle in the UI | |
MAX_CLIPS = 10 | |
def process_script(topic, script_input): | |
"""Process the topic or script and return updates for the UI.""" | |
if script_input.strip(): | |
raw_script = script_input | |
else: | |
raw_script = generate_script(topic) | |
if not raw_script: | |
return "Failed to generate script", 0, [], [], [], [] | |
elements = parse_script(raw_script) | |
paired_elements = [(elements[i], elements[i + 1]) for i in range(0, len(elements) - 1, 2)] | |
num_clips = min(len(paired_elements), MAX_CLIPS) | |
# Prepare updates for clip editor | |
accordion_updates = [] | |
prompt_updates = [] | |
narration_updates = [] | |
media_updates = [] | |
for i in range(MAX_CLIPS): | |
if i < num_clips: | |
media_elem, tts_elem = paired_elements[i] | |
accordion_updates.append(gr.update(visible=True, label=f"Clip {i+1}: {media_elem['prompt'][:20]}...")) | |
prompt_updates.append(gr.update(value=media_elem['prompt'])) | |
narration_updates.append(gr.update(value=tts_elem['text'])) | |
media_updates.append(gr.update(value=None)) # Reset file upload | |
else: | |
accordion_updates.append(gr.update(visible=False)) | |
prompt_updates.append(gr.update(value="")) | |
narration_updates.append(gr.update(value="")) | |
media_updates.append(gr.update(value=None)) | |
return raw_script, num_clips, accordion_updates, prompt_updates, narration_updates, media_updates | |
def generate_video_full(resolution, render_speed, video_clip_percent, zoom_pan_effect, | |
bgm_upload, bgm_volume, subtitles_enabled, font, font_size, | |
outline_width, font_color, outline_color, position, num_clips, | |
*clip_inputs): | |
"""Generate the video using all settings and edited clip data.""" | |
global TARGET_RESOLUTION, CAPTION_COLOR, TEMP_FOLDER | |
# Set resolution | |
TARGET_RESOLUTION = (1080, 1920) if resolution == "Short (1080x1920)" else (1920, 1080) | |
# Set caption settings | |
CAPTION_COLOR = font_color if subtitles_enabled else "transparent" | |
# Create temporary folder | |
TEMP_FOLDER = tempfile.mkdtemp() | |
# Parse clip inputs (visual_prompt, narration, custom_media for each clip) | |
clips_data = [] | |
for i in range(num_clips): | |
idx = i * 3 | |
visual_prompt = clip_inputs[idx] | |
narration = clip_inputs[idx + 1] | |
custom_media = clip_inputs[idx + 2] | |
clips_data.append({ | |
'visual_prompt': visual_prompt, | |
'narration': narration, | |
'custom_media': custom_media | |
}) | |
# Generate clips | |
clips = [] | |
for idx, clip_data in enumerate(clips_data): | |
# Use custom media if provided, otherwise generate media | |
if clip_data['custom_media']: | |
media_path = clip_data['custom_media'] | |
asset_type = 'video' if media_path.endswith(('.mp4', '.avi', '.mov')) else 'image' | |
else: | |
media_asset = generate_media(clip_data['visual_prompt'], current_index=idx, total_segments=num_clips) | |
if not media_asset: | |
continue | |
media_path = media_asset['path'] | |
asset_type = media_asset['asset_type'] | |
# Adjust video clip percentage | |
original_random = random.random() | |
adjusted_random = original_random * (video_clip_percent / 100) | |
if adjusted_random < (video_clip_percent / 100) and not clip_data['custom_media']: | |
media_asset = generate_media(clip_data['visual_prompt'], current_index=idx, total_segments=num_clips) | |
if media_asset and media_asset['asset_type'] == 'video': | |
media_path = media_asset['path'] | |
asset_type = 'video' | |
# Generate TTS | |
tts_path = generate_tts(clip_data['narration'], 'en') | |
if not tts_path: | |
continue | |
# Create clip | |
duration = max(3, len(clip_data['narration'].split()) * 0.5) | |
clip = create_clip( | |
media_path=media_path, | |
asset_type=asset_type, | |
tts_path=tts_path, | |
duration=duration, | |
effects='fade-in', | |
narration_text=clip_data['narration'], | |
segment_index=idx | |
) | |
if clip and zoom_pan_effect and asset_type == 'image': | |
clip = apply_kenburns_effect(clip, TARGET_RESOLUTION) | |
if clip: | |
clips.append(clip) | |
if not clips: | |
shutil.rmtree(TEMP_FOLDER) | |
return None, None | |
# Concatenate clips | |
final_video = concatenate_videoclips(clips, method="compose") | |
# Add background music if uploaded | |
if bgm_upload: | |
bg_music = AudioFileClip(bgm_upload).volumex(bgm_volume) | |
if bg_music.duration < final_video.duration: | |
bg_music = bg_music.loop(duration=final_video.duration) | |
else: | |
bg_music = bg_music.subclip(0, final_video.duration) | |
final_video = final_video.set_audio(CompositeVideoClip([final_video.audio, bg_music])) | |
# Export video | |
output_path = "final_video.mp4" | |
final_video.write_videofile(output_path, codec='libx264', fps=24, preset=render_speed) | |
# Clean up | |
shutil.rmtree(TEMP_FOLDER) | |
return output_path, output_path | |
# Gradio Blocks Interface | |
with gr.Blocks(title="π Orbit Video Engine") as demo: | |
gr.Markdown("# π Orbit Video Engine") | |
gr.Markdown("Create funny documentary-style videos with ease!") | |
with gr.Row(): | |
# Column 1: Content Input & Script Generation | |
with gr.Column(scale=1): | |
gr.Markdown("### 1. Content Input") | |
topic_input = gr.Textbox(label="Topic", placeholder="e.g., Funny Cat Facts") | |
script_input = gr.Textbox(label="Or Paste Full Script", lines=10, placeholder="[Title]\nNarration...") | |
generate_button = gr.Button("π Generate Script & Load Clips") | |
script_display = gr.Textbox(label="Generated Script", interactive=False, visible=False) | |
# Column 2: Clip Editor | |
with gr.Column(scale=2): | |
gr.Markdown("### 2. Edit Clips") | |
gr.Markdown("Modify prompts, narration, or upload custom media below.") | |
with gr.Column() as clip_editor: | |
clip_accordions = [] | |
for i in range(MAX_CLIPS): | |
with gr.Accordion(f"Clip {i+1}", visible=False) as acc: | |
visual_prompt = gr.Textbox(label="Visual Prompt") | |
narration = gr.Textbox(label="Narration", lines=3) | |
custom_media = gr.File(label="Upload Custom Media (Image/Video)") | |
clip_accordions.append((acc, visual_prompt, narration, custom_media)) | |
# Column 3: Settings & Output | |
with gr.Column(scale=1): | |
gr.Markdown("### 3. Video Settings") | |
resolution = gr.Radio(["Short (1080x1920)", "Full HD (1920x1080)"], label="Resolution", value="Full HD (1920x1080)") | |
render_speed = gr.Dropdown(["ultrafast", "faster", "fast", "medium", "slow", "slower", "veryslow"], label="Render Speed", value="fast") | |
video_clip_percent = gr.Slider(0, 100, value=25, label="Video Clip Percentage") | |
zoom_pan_effect = gr.Checkbox(label="Add Zoom/Pan Effect (Images)", value=True) | |
with gr.Accordion("Background Music", open=False): | |
bgm_upload = gr.Audio(label="Upload Background Music", type="filepath") | |
bgm_volume = gr.Slider(0.0, 1.0, value=0.15, label="BGM Volume") | |
with gr.Accordion("Subtitle Settings", open=True): | |
subtitles_enabled = gr.Checkbox(label="Enable Subtitles", value=True) | |
font = gr.Dropdown(["Impact", "Arial", "Times New Roman"], label="Font", value="Arial") | |
font_size = gr.Number(label="Font Size", value=45) | |
outline_width = gr.Number(label="Outline Width", value=2) | |
font_color = gr.ColorPicker(label="Font Color", value="#FFFFFF") | |
outline_color = gr.ColorPicker(label="Outline Color", value="#000000") | |
position = gr.Radio(["center", "bottom", "top"], label="Position", value="bottom") | |
generate_video_button = gr.Button("π¬ Generate Video") | |
gr.Markdown("### 4. Output") | |
output_video = gr.Video(label="Generated Video") | |
download_button = gr.File(label="Download Video") | |
# State to track number of clips | |
num_clips_state = gr.State(value=0) | |
# Event handlers | |
generate_button.click( | |
fn=process_script, | |
inputs=[topic_input, script_input], | |
outputs=[script_display, num_clips_state] + | |
[comp for acc in clip_accordions for comp in [acc[0], acc[1], acc[2], acc[3]]], | |
_js="() => {return [document.querySelector('#topic_input textarea').value, document.querySelector('#script_input textarea').value]}" | |
).then( | |
fn=lambda x: gr.update(visible=True), | |
inputs=[script_display], | |
outputs=[script_display] | |
) | |
generate_video_button.click( | |
fn=generate_video_full, | |
inputs=[resolution, render_speed, video_clip_percent, zoom_pan_effect, | |
bgm_upload, bgm_volume, subtitles_enabled, font, font_size, | |
outline_width, font_color, outline_color, position, num_clips_state] + | |
[comp for acc in clip_accordions for comp in acc[1:]], # visual_prompt, narration, custom_media | |
outputs=[output_video, download_button] | |
) | |
# Launch the interface | |
demo.launch(share=True) |