testdeep123 commited on
Commit
32c6d88
·
verified ·
1 Parent(s): 3e3c550

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +129 -96
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
- from moviepy.editor import concatenate_videoclips, CompositeVideoClip, AudioFileClip, TextClip
 
 
 
 
 
7
 
8
- # Your existing helper functions (generate_script, parse_script, etc.) remain unchanged
9
- # Ensure TEMP_FOLDER, TARGET_RESOLUTION, and CAPTION_COLOR are set within functions as needed
10
-
11
- # Define maximum number of clips to handle in the UI
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)) # Reset file upload
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, font, font_size,
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': visual_prompt,
72
- 'narration': narration,
73
- 'custom_media': 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'], current_index=idx, total_segments=num_clips)
85
- if not media_asset:
86
- continue
87
  media_path = media_asset['path']
88
  asset_type = media_asset['asset_type']
89
-
90
- # Adjust video clip percentage
91
- original_random = random.random()
92
- adjusted_random = original_random * (video_clip_percent / 100)
93
- if adjusted_random < (video_clip_percent / 100) and not clip_data['custom_media']:
94
- media_asset = generate_media(clip_data['visual_prompt'], current_index=idx, total_segments=num_clips)
95
- if media_asset and media_asset['asset_type'] == 'video':
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
- # Export video
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 Blocks Interface
146
- with gr.Blocks(title="🚀 Orbit Video Engine") as demo:
147
- gr.Markdown("# 🚀 Orbit Video Engine")
148
- gr.Markdown("Create funny documentary-style videos with ease!")
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("📝 Generate Script & Load Clips")
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.Markdown("Modify prompts, narration, or upload custom media below.")
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 (Image/Video)")
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", "faster", "fast", "medium", "slow", "slower", "veryslow"], label="Render Speed", value="fast")
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 (Images)", value=True)
179
-
180
- with gr.Accordion("Background Music", open=False):
181
- bgm_upload = gr.Audio(label="Upload Background Music", type="filepath")
182
- bgm_volume = gr.Slider(0.0, 1.0, value=0.15, label="BGM Volume")
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, font, font_size,
219
- outline_width, font_color, outline_color, position, num_clips_state] +
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
- # Launch the interface
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()