Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,15 +1,104 @@
|
|
1 |
-
# Import necessary libraries (assuming all your imports remain the same)
|
2 |
import gradio as gr
|
3 |
import os
|
4 |
import tempfile
|
5 |
import shutil
|
6 |
-
|
|
|
|
|
|
|
|
|
|
|
7 |
|
8 |
-
#
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
MAX_CLIPS = 10
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
|
14 |
def process_script(topic, script_input):
|
15 |
"""Process the topic or script and return updates for the UI."""
|
@@ -24,7 +113,6 @@ def process_script(topic, script_input):
|
|
24 |
paired_elements = [(elements[i], elements[i + 1]) for i in range(0, len(elements) - 1, 2)]
|
25 |
num_clips = min(len(paired_elements), MAX_CLIPS)
|
26 |
|
27 |
-
# Prepare updates for clip editor
|
28 |
accordion_updates = []
|
29 |
prompt_updates = []
|
30 |
narration_updates = []
|
@@ -35,7 +123,7 @@ def process_script(topic, script_input):
|
|
35 |
accordion_updates.append(gr.update(visible=True, label=f"Clip {i+1}: {media_elem['prompt'][:20]}..."))
|
36 |
prompt_updates.append(gr.update(value=media_elem['prompt']))
|
37 |
narration_updates.append(gr.update(value=tts_elem['text']))
|
38 |
-
media_updates.append(gr.update(value=None))
|
39 |
else:
|
40 |
accordion_updates.append(gr.update(visible=False))
|
41 |
prompt_updates.append(gr.update(value=""))
|
@@ -45,73 +133,41 @@ def process_script(topic, script_input):
|
|
45 |
return raw_script, num_clips, accordion_updates, prompt_updates, narration_updates, media_updates
|
46 |
|
47 |
def generate_video_full(resolution, render_speed, video_clip_percent, zoom_pan_effect,
|
48 |
-
bgm_upload, bgm_volume, subtitles_enabled,
|
49 |
-
outline_width, font_color, outline_color, position, num_clips,
|
50 |
-
*clip_inputs):
|
51 |
"""Generate the video using all settings and edited clip data."""
|
52 |
global TARGET_RESOLUTION, CAPTION_COLOR, TEMP_FOLDER
|
53 |
|
54 |
-
# Set resolution
|
55 |
TARGET_RESOLUTION = (1080, 1920) if resolution == "Short (1080x1920)" else (1920, 1080)
|
56 |
-
|
57 |
-
# Set caption settings
|
58 |
-
CAPTION_COLOR = font_color if subtitles_enabled else "transparent"
|
59 |
-
|
60 |
-
# Create temporary folder
|
61 |
TEMP_FOLDER = tempfile.mkdtemp()
|
62 |
|
63 |
-
# Parse clip inputs (visual_prompt, narration, custom_media for each clip)
|
64 |
clips_data = []
|
65 |
for i in range(num_clips):
|
66 |
idx = i * 3
|
67 |
-
visual_prompt = clip_inputs[idx]
|
68 |
-
narration = clip_inputs[idx + 1]
|
69 |
-
custom_media = clip_inputs[idx + 2]
|
70 |
clips_data.append({
|
71 |
-
'visual_prompt':
|
72 |
-
'narration':
|
73 |
-
'custom_media':
|
74 |
})
|
75 |
|
76 |
-
# Generate clips
|
77 |
clips = []
|
78 |
for idx, clip_data in enumerate(clips_data):
|
79 |
-
# Use custom media if provided, otherwise generate media
|
80 |
if clip_data['custom_media']:
|
81 |
media_path = clip_data['custom_media']
|
82 |
asset_type = 'video' if media_path.endswith(('.mp4', '.avi', '.mov')) else 'image'
|
83 |
else:
|
84 |
-
media_asset = generate_media(clip_data['visual_prompt'],
|
85 |
-
if not media_asset:
|
86 |
-
continue
|
87 |
media_path = media_asset['path']
|
88 |
asset_type = media_asset['asset_type']
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
media_path = media_asset['path']
|
97 |
-
asset_type = 'video'
|
98 |
-
|
99 |
-
# Generate TTS
|
100 |
-
tts_path = generate_tts(clip_data['narration'], 'en')
|
101 |
-
if not tts_path:
|
102 |
-
continue
|
103 |
-
|
104 |
-
# Create clip
|
105 |
duration = max(3, len(clip_data['narration'].split()) * 0.5)
|
106 |
-
clip = create_clip(
|
107 |
-
media_path=media_path,
|
108 |
-
asset_type=asset_type,
|
109 |
-
tts_path=tts_path,
|
110 |
-
duration=duration,
|
111 |
-
effects='fade-in',
|
112 |
-
narration_text=clip_data['narration'],
|
113 |
-
segment_index=idx
|
114 |
-
)
|
115 |
if clip and zoom_pan_effect and asset_type == 'image':
|
116 |
clip = apply_kenburns_effect(clip, TARGET_RESOLUTION)
|
117 |
if clip:
|
@@ -121,10 +177,8 @@ def generate_video_full(resolution, render_speed, video_clip_percent, zoom_pan_e
|
|
121 |
shutil.rmtree(TEMP_FOLDER)
|
122 |
return None, None
|
123 |
|
124 |
-
# Concatenate clips
|
125 |
final_video = concatenate_videoclips(clips, method="compose")
|
126 |
-
|
127 |
-
# Add background music if uploaded
|
128 |
if bgm_upload:
|
129 |
bg_music = AudioFileClip(bgm_upload).volumex(bgm_volume)
|
130 |
if bg_music.duration < final_video.duration:
|
@@ -133,19 +187,16 @@ def generate_video_full(resolution, render_speed, video_clip_percent, zoom_pan_e
|
|
133 |
bg_music = bg_music.subclip(0, final_video.duration)
|
134 |
final_video = final_video.set_audio(CompositeVideoClip([final_video.audio, bg_music]))
|
135 |
|
136 |
-
|
137 |
-
output_path = "final_video.mp4"
|
138 |
final_video.write_videofile(output_path, codec='libx264', fps=24, preset=render_speed)
|
139 |
-
|
140 |
-
# Clean up
|
141 |
shutil.rmtree(TEMP_FOLDER)
|
142 |
|
143 |
return output_path, output_path
|
144 |
|
145 |
-
# Gradio
|
146 |
-
with gr.Blocks(title="
|
147 |
-
gr.Markdown("#
|
148 |
-
gr.Markdown("Create
|
149 |
|
150 |
with gr.Row():
|
151 |
# Column 1: Content Input & Script Generation
|
@@ -153,59 +204,43 @@ with gr.Blocks(title="🚀 Orbit Video Engine") as demo:
|
|
153 |
gr.Markdown("### 1. Content Input")
|
154 |
topic_input = gr.Textbox(label="Topic", placeholder="e.g., Funny Cat Facts")
|
155 |
script_input = gr.Textbox(label="Or Paste Full Script", lines=10, placeholder="[Title]\nNarration...")
|
156 |
-
generate_button = gr.Button("
|
157 |
script_display = gr.Textbox(label="Generated Script", interactive=False, visible=False)
|
158 |
|
159 |
# Column 2: Clip Editor
|
160 |
with gr.Column(scale=2):
|
161 |
gr.Markdown("### 2. Edit Clips")
|
162 |
-
gr.
|
163 |
-
with gr.Column() as clip_editor:
|
164 |
clip_accordions = []
|
165 |
for i in range(MAX_CLIPS):
|
166 |
with gr.Accordion(f"Clip {i+1}", visible=False) as acc:
|
167 |
visual_prompt = gr.Textbox(label="Visual Prompt")
|
168 |
narration = gr.Textbox(label="Narration", lines=3)
|
169 |
-
custom_media = gr.File(label="Upload Custom Media
|
170 |
clip_accordions.append((acc, visual_prompt, narration, custom_media))
|
171 |
|
172 |
# Column 3: Settings & Output
|
173 |
with gr.Column(scale=1):
|
174 |
gr.Markdown("### 3. Video Settings")
|
175 |
resolution = gr.Radio(["Short (1080x1920)", "Full HD (1920x1080)"], label="Resolution", value="Full HD (1920x1080)")
|
176 |
-
render_speed = gr.Dropdown(["ultrafast", "
|
177 |
video_clip_percent = gr.Slider(0, 100, value=25, label="Video Clip Percentage")
|
178 |
-
zoom_pan_effect = gr.Checkbox(label="Add Zoom/Pan Effect
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
with gr.Accordion("Subtitle Settings", open=True):
|
185 |
-
subtitles_enabled = gr.Checkbox(label="Enable Subtitles", value=True)
|
186 |
-
font = gr.Dropdown(["Impact", "Arial", "Times New Roman"], label="Font", value="Arial")
|
187 |
-
font_size = gr.Number(label="Font Size", value=45)
|
188 |
-
outline_width = gr.Number(label="Outline Width", value=2)
|
189 |
-
font_color = gr.ColorPicker(label="Font Color", value="#FFFFFF")
|
190 |
-
outline_color = gr.ColorPicker(label="Outline Color", value="#000000")
|
191 |
-
position = gr.Radio(["center", "bottom", "top"], label="Position", value="bottom")
|
192 |
-
|
193 |
-
generate_video_button = gr.Button("🎬 Generate Video")
|
194 |
-
|
195 |
gr.Markdown("### 4. Output")
|
196 |
output_video = gr.Video(label="Generated Video")
|
197 |
download_button = gr.File(label="Download Video")
|
198 |
|
199 |
-
# State to track number of clips
|
200 |
num_clips_state = gr.State(value=0)
|
201 |
|
202 |
-
# Event handlers
|
203 |
generate_button.click(
|
204 |
fn=process_script,
|
205 |
inputs=[topic_input, script_input],
|
206 |
outputs=[script_display, num_clips_state] +
|
207 |
-
[comp for acc in clip_accordions for comp in [acc[0], acc[1], acc[2], acc[3]]]
|
208 |
-
_js="() => {return [document.querySelector('#topic_input textarea').value, document.querySelector('#script_input textarea').value]}"
|
209 |
).then(
|
210 |
fn=lambda x: gr.update(visible=True),
|
211 |
inputs=[script_display],
|
@@ -215,11 +250,9 @@ with gr.Blocks(title="🚀 Orbit Video Engine") as demo:
|
|
215 |
generate_video_button.click(
|
216 |
fn=generate_video_full,
|
217 |
inputs=[resolution, render_speed, video_clip_percent, zoom_pan_effect,
|
218 |
-
bgm_upload, bgm_volume, subtitles_enabled,
|
219 |
-
|
220 |
-
[comp for acc in clip_accordions for comp in acc[1:]], # visual_prompt, narration, custom_media
|
221 |
outputs=[output_video, download_button]
|
222 |
)
|
223 |
|
224 |
-
|
225 |
-
demo.launch(share=True)
|
|
|
|
|
1 |
import gradio as gr
|
2 |
import os
|
3 |
import tempfile
|
4 |
import shutil
|
5 |
+
import random
|
6 |
+
import requests
|
7 |
+
from moviepy.editor import VideoFileClip, concatenate_videoclips, AudioFileClip, ImageClip, TextClip
|
8 |
+
import numpy as np
|
9 |
+
from gtts import gTTS
|
10 |
+
import cv2
|
11 |
|
12 |
+
# Global configurations
|
13 |
+
PEXELS_API_KEY = 'BhJqbcdm9Vi90KqzXKAhnEHGsuFNv4irXuOjWtT761U49lRzo03qBGna' # Replace with your Pexels API key
|
14 |
+
OUTPUT_VIDEO_FILENAME = "final_video.mp4"
|
15 |
+
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
|
16 |
MAX_CLIPS = 10
|
17 |
+
TARGET_RESOLUTION = (1920, 1080) # Default resolution
|
18 |
+
TEMP_FOLDER = None
|
19 |
+
CAPTION_COLOR = "#FFFFFF"
|
20 |
+
|
21 |
+
# Helper Functions
|
22 |
+
def generate_script(topic):
|
23 |
+
"""Generate a simple script based on the topic."""
|
24 |
+
return f"[Title]\n{topic}\n[Narration]\nThis is a sample narration about {topic}."
|
25 |
+
|
26 |
+
def parse_script(script):
|
27 |
+
"""Parse the script into elements."""
|
28 |
+
elements = []
|
29 |
+
lines = script.strip().split('\n')
|
30 |
+
for line in lines:
|
31 |
+
if line.startswith('[Title]'):
|
32 |
+
elements.append({'type': 'media', 'prompt': line[7:].strip()})
|
33 |
+
elif line.startswith('[Narration]'):
|
34 |
+
elements.append({'type': 'tts', 'text': line[11:].strip()})
|
35 |
+
return elements
|
36 |
+
|
37 |
+
def search_pexels_videos(query, api_key):
|
38 |
+
"""Search for videos on Pexels."""
|
39 |
+
url = f"https://api.pexels.com/videos/search?query={quote(query)}&per_page=1"
|
40 |
+
headers = {"Authorization": api_key}
|
41 |
+
response = requests.get(url, headers=headers)
|
42 |
+
if response.status_code == 200:
|
43 |
+
data = response.json()
|
44 |
+
if data['videos']:
|
45 |
+
return data['videos'][0]['video_files'][0]['link']
|
46 |
+
return None
|
47 |
+
|
48 |
+
def download_video(url, output_path):
|
49 |
+
"""Download a video from a URL."""
|
50 |
+
response = requests.get(url, headers={"User-Agent": USER_AGENT}, stream=True)
|
51 |
+
if response.status_code == 200:
|
52 |
+
with open(output_path, 'wb') as f:
|
53 |
+
shutil.copyfileobj(response.raw, f)
|
54 |
+
return output_path
|
55 |
+
return None
|
56 |
+
|
57 |
+
def generate_tts(text, lang='en'):
|
58 |
+
"""Generate TTS audio using gTTS."""
|
59 |
+
tts = gTTS(text=text, lang=lang, slow=False)
|
60 |
+
tts_path = os.path.join(TEMP_FOLDER, f"tts_{random.randint(0, 10000)}.mp3")
|
61 |
+
tts.save(tts_path)
|
62 |
+
return tts_path
|
63 |
+
|
64 |
+
def resize_media(media_path, target_resolution):
|
65 |
+
"""Resize media to match target resolution."""
|
66 |
+
clip = VideoFileClip(media_path) if media_path.endswith(('.mp4', '.avi', '.mov')) else ImageClip(media_path)
|
67 |
+
return clip.resize(target_resolution)
|
68 |
+
|
69 |
+
def apply_kenburns_effect(clip, target_resolution):
|
70 |
+
"""Apply a zoom/pan (Ken Burns) effect to an image clip."""
|
71 |
+
w, h = target_resolution
|
72 |
+
clip = clip.resize(height=h * 1.2).crop(x_center=w/2, y_center=h/2, width=w, height=h)
|
73 |
+
return clip.fx(vfx.zoom_in, 0.1)
|
74 |
+
|
75 |
+
def create_clip(media_path, asset_type, tts_path, duration, effects, narration_text, segment_index):
|
76 |
+
"""Create a video clip with media and narration."""
|
77 |
+
if asset_type == 'video':
|
78 |
+
clip = VideoFileClip(media_path).subclip(0, min(duration, VideoFileClip(media_path).duration))
|
79 |
+
else:
|
80 |
+
clip = ImageClip(media_path, duration=duration)
|
81 |
+
|
82 |
+
clip = resize_media(media_path, TARGET_RESOLUTION)
|
83 |
+
if effects == 'fade-in':
|
84 |
+
clip = clip.crossfadein(1.0)
|
85 |
+
|
86 |
+
audio = AudioFileClip(tts_path)
|
87 |
+
clip = clip.set_audio(audio.set_duration(duration))
|
88 |
+
|
89 |
+
if CAPTION_COLOR != "transparent":
|
90 |
+
txt_clip = TextClip(narration_text, fontsize=45, color=CAPTION_COLOR, font="Arial", stroke_color="#000000", stroke_width=2)
|
91 |
+
txt_clip = txt_clip.set_position('bottom').set_duration(duration)
|
92 |
+
clip = CompositeVideoClip([clip, txt_clip])
|
93 |
+
|
94 |
+
return clip
|
95 |
+
|
96 |
+
def generate_media(prompt, current_index, total_segments):
|
97 |
+
"""Generate media (placeholder for actual generation)."""
|
98 |
+
media_path = os.path.join(TEMP_FOLDER, f"media_{current_index}.jpg")
|
99 |
+
with open(media_path, 'wb') as f:
|
100 |
+
f.write(requests.get("https://via.placeholder.com/1920x1080").content) # Placeholder image
|
101 |
+
return {'path': media_path, 'asset_type': 'image'}
|
102 |
|
103 |
def process_script(topic, script_input):
|
104 |
"""Process the topic or script and return updates for the UI."""
|
|
|
113 |
paired_elements = [(elements[i], elements[i + 1]) for i in range(0, len(elements) - 1, 2)]
|
114 |
num_clips = min(len(paired_elements), MAX_CLIPS)
|
115 |
|
|
|
116 |
accordion_updates = []
|
117 |
prompt_updates = []
|
118 |
narration_updates = []
|
|
|
123 |
accordion_updates.append(gr.update(visible=True, label=f"Clip {i+1}: {media_elem['prompt'][:20]}..."))
|
124 |
prompt_updates.append(gr.update(value=media_elem['prompt']))
|
125 |
narration_updates.append(gr.update(value=tts_elem['text']))
|
126 |
+
media_updates.append(gr.update(value=None))
|
127 |
else:
|
128 |
accordion_updates.append(gr.update(visible=False))
|
129 |
prompt_updates.append(gr.update(value=""))
|
|
|
133 |
return raw_script, num_clips, accordion_updates, prompt_updates, narration_updates, media_updates
|
134 |
|
135 |
def generate_video_full(resolution, render_speed, video_clip_percent, zoom_pan_effect,
|
136 |
+
bgm_upload, bgm_volume, subtitles_enabled, num_clips, *clip_inputs):
|
|
|
|
|
137 |
"""Generate the video using all settings and edited clip data."""
|
138 |
global TARGET_RESOLUTION, CAPTION_COLOR, TEMP_FOLDER
|
139 |
|
|
|
140 |
TARGET_RESOLUTION = (1080, 1920) if resolution == "Short (1080x1920)" else (1920, 1080)
|
141 |
+
CAPTION_COLOR = "#FFFFFF" if subtitles_enabled else "transparent"
|
|
|
|
|
|
|
|
|
142 |
TEMP_FOLDER = tempfile.mkdtemp()
|
143 |
|
|
|
144 |
clips_data = []
|
145 |
for i in range(num_clips):
|
146 |
idx = i * 3
|
|
|
|
|
|
|
147 |
clips_data.append({
|
148 |
+
'visual_prompt': clip_inputs[idx],
|
149 |
+
'narration': clip_inputs[idx + 1],
|
150 |
+
'custom_media': clip_inputs[idx + 2]
|
151 |
})
|
152 |
|
|
|
153 |
clips = []
|
154 |
for idx, clip_data in enumerate(clips_data):
|
|
|
155 |
if clip_data['custom_media']:
|
156 |
media_path = clip_data['custom_media']
|
157 |
asset_type = 'video' if media_path.endswith(('.mp4', '.avi', '.mov')) else 'image'
|
158 |
else:
|
159 |
+
media_asset = generate_media(clip_data['visual_prompt'], idx, num_clips)
|
|
|
|
|
160 |
media_path = media_asset['path']
|
161 |
asset_type = media_asset['asset_type']
|
162 |
+
if random.random() < (video_clip_percent / 100):
|
163 |
+
video_url = search_pexels_videos(clip_data['visual_prompt'], PEXELS_API_KEY)
|
164 |
+
if video_url:
|
165 |
+
media_path = download_video(video_url, os.path.join(TEMP_FOLDER, f"video_{idx}.mp4"))
|
166 |
+
asset_type = 'video'
|
167 |
+
|
168 |
+
tts_path = generate_tts(clip_data['narration'])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
169 |
duration = max(3, len(clip_data['narration'].split()) * 0.5)
|
170 |
+
clip = create_clip(media_path, asset_type, tts_path, duration, 'fade-in', clip_data['narration'], idx)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
171 |
if clip and zoom_pan_effect and asset_type == 'image':
|
172 |
clip = apply_kenburns_effect(clip, TARGET_RESOLUTION)
|
173 |
if clip:
|
|
|
177 |
shutil.rmtree(TEMP_FOLDER)
|
178 |
return None, None
|
179 |
|
|
|
180 |
final_video = concatenate_videoclips(clips, method="compose")
|
181 |
+
|
|
|
182 |
if bgm_upload:
|
183 |
bg_music = AudioFileClip(bgm_upload).volumex(bgm_volume)
|
184 |
if bg_music.duration < final_video.duration:
|
|
|
187 |
bg_music = bg_music.subclip(0, final_video.duration)
|
188 |
final_video = final_video.set_audio(CompositeVideoClip([final_video.audio, bg_music]))
|
189 |
|
190 |
+
output_path = OUTPUT_VIDEO_FILENAME
|
|
|
191 |
final_video.write_videofile(output_path, codec='libx264', fps=24, preset=render_speed)
|
|
|
|
|
192 |
shutil.rmtree(TEMP_FOLDER)
|
193 |
|
194 |
return output_path, output_path
|
195 |
|
196 |
+
# Gradio Interface
|
197 |
+
with gr.Blocks(title="Video Generator") as demo:
|
198 |
+
gr.Markdown("# Video Generator")
|
199 |
+
gr.Markdown("Create custom videos with script, clips, and settings!")
|
200 |
|
201 |
with gr.Row():
|
202 |
# Column 1: Content Input & Script Generation
|
|
|
204 |
gr.Markdown("### 1. Content Input")
|
205 |
topic_input = gr.Textbox(label="Topic", placeholder="e.g., Funny Cat Facts")
|
206 |
script_input = gr.Textbox(label="Or Paste Full Script", lines=10, placeholder="[Title]\nNarration...")
|
207 |
+
generate_button = gr.Button("Generate Script & Load Clips")
|
208 |
script_display = gr.Textbox(label="Generated Script", interactive=False, visible=False)
|
209 |
|
210 |
# Column 2: Clip Editor
|
211 |
with gr.Column(scale=2):
|
212 |
gr.Markdown("### 2. Edit Clips")
|
213 |
+
with gr.Column():
|
|
|
214 |
clip_accordions = []
|
215 |
for i in range(MAX_CLIPS):
|
216 |
with gr.Accordion(f"Clip {i+1}", visible=False) as acc:
|
217 |
visual_prompt = gr.Textbox(label="Visual Prompt")
|
218 |
narration = gr.Textbox(label="Narration", lines=3)
|
219 |
+
custom_media = gr.File(label="Upload Custom Media")
|
220 |
clip_accordions.append((acc, visual_prompt, narration, custom_media))
|
221 |
|
222 |
# Column 3: Settings & Output
|
223 |
with gr.Column(scale=1):
|
224 |
gr.Markdown("### 3. Video Settings")
|
225 |
resolution = gr.Radio(["Short (1080x1920)", "Full HD (1920x1080)"], label="Resolution", value="Full HD (1920x1080)")
|
226 |
+
render_speed = gr.Dropdown(["ultrafast", "fast", "medium", "slow"], label="Render Speed", value="fast")
|
227 |
video_clip_percent = gr.Slider(0, 100, value=25, label="Video Clip Percentage")
|
228 |
+
zoom_pan_effect = gr.Checkbox(label="Add Zoom/Pan Effect", value=True)
|
229 |
+
bgm_upload = gr.Audio(label="Upload Background Music", type="filepath")
|
230 |
+
bgm_volume = gr.Slider(0.0, 1.0, value=0.15, label="BGM Volume")
|
231 |
+
subtitles_enabled = gr.Checkbox(label="Enable Subtitles", value=True)
|
232 |
+
generate_video_button = gr.Button("Generate Video")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
233 |
gr.Markdown("### 4. Output")
|
234 |
output_video = gr.Video(label="Generated Video")
|
235 |
download_button = gr.File(label="Download Video")
|
236 |
|
|
|
237 |
num_clips_state = gr.State(value=0)
|
238 |
|
|
|
239 |
generate_button.click(
|
240 |
fn=process_script,
|
241 |
inputs=[topic_input, script_input],
|
242 |
outputs=[script_display, num_clips_state] +
|
243 |
+
[comp for acc in clip_accordions for comp in [acc[0], acc[1], acc[2], acc[3]]]
|
|
|
244 |
).then(
|
245 |
fn=lambda x: gr.update(visible=True),
|
246 |
inputs=[script_display],
|
|
|
250 |
generate_video_button.click(
|
251 |
fn=generate_video_full,
|
252 |
inputs=[resolution, render_speed, video_clip_percent, zoom_pan_effect,
|
253 |
+
bgm_upload, bgm_volume, subtitles_enabled, num_clips_state] +
|
254 |
+
[comp for acc in clip_accordions for comp in acc[1:]],
|
|
|
255 |
outputs=[output_video, download_button]
|
256 |
)
|
257 |
|
258 |
+
demo.launch()
|
|