Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -285,108 +285,108 @@ class EnhancedVideoGenerator:
|
|
| 285 |
return AudioFileClip(duration=len(script.split()) * 0.3)
|
| 286 |
|
| 287 |
def create_video(self, script: str, style: str, duration: int, output_path: str) -> str:
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
frame_futures = []
|
| 320 |
-
for i in range(start_frame, end_frame):
|
| 321 |
-
progress = i / total_frames
|
| 322 |
-
text_index = int(progress * len(script.split()))
|
| 323 |
-
current_text = " ".join(script.split()[:text_index + 1])
|
| 324 |
|
| 325 |
-
|
| 326 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 327 |
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
output_path
|
| 354 |
-
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
'type': 'image',
|
| 381 |
-
'data': img,
|
| 382 |
-
'topic': topic
|
| 383 |
-
})
|
| 384 |
-
|
| 385 |
-
return assets
|
| 386 |
-
|
| 387 |
-
except Exception as e:
|
| 388 |
-
self.logger.error(f"Visual asset generation failed: {str(e)}")
|
| 389 |
-
return []
|
| 390 |
|
| 391 |
@staticmethod
|
| 392 |
def clean_text(text: str) -> str:
|
|
|
|
| 285 |
return AudioFileClip(duration=len(script.split()) * 0.3)
|
| 286 |
|
| 287 |
def create_video(self, script: str, style: str, duration: int, output_path: str) -> str:
|
| 288 |
+
"""Create full video with all enhanced features"""
|
| 289 |
+
try:
|
| 290 |
+
# Progress bar
|
| 291 |
+
progress_bar = st.progress(0)
|
| 292 |
+
status_text = st.empty()
|
| 293 |
+
|
| 294 |
+
# Generate visual assets (20%)
|
| 295 |
+
status_text.text("Generating visual assets...")
|
| 296 |
+
assets = self.generate_visual_assets(script, style)
|
| 297 |
+
progress_bar.progress(20)
|
| 298 |
+
|
| 299 |
+
# Generate voice-over (40%)
|
| 300 |
+
status_text.text("Creating voice-over...")
|
| 301 |
+
audio = self.generate_voice_over(script)
|
| 302 |
+
progress_bar.progress(40)
|
| 303 |
+
|
| 304 |
+
# Create frames
|
| 305 |
+
fps = 30
|
| 306 |
+
total_frames = int(duration * fps)
|
| 307 |
+
frames = []
|
| 308 |
+
|
| 309 |
+
# Use smaller batch size for better memory management
|
| 310 |
+
batch_size = 10
|
| 311 |
+
num_batches = (total_frames + batch_size - 1) // batch_size
|
| 312 |
+
|
| 313 |
+
with ThreadPoolExecutor(max_workers=4) as executor:
|
| 314 |
+
for batch in range(num_batches):
|
| 315 |
+
status_text.text(f"Generating frames: {batch*batch_size}/{total_frames}")
|
| 316 |
+
start_frame = batch * batch_size
|
| 317 |
+
end_frame = min((batch + 1) * batch_size, total_frames)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 318 |
|
| 319 |
+
frame_futures = []
|
| 320 |
+
for i in range(start_frame, end_frame):
|
| 321 |
+
progress = i / total_frames
|
| 322 |
+
text_index = int(progress * len(script.split()))
|
| 323 |
+
current_text = " ".join(script.split()[:text_index + 1])
|
| 324 |
+
|
| 325 |
+
asset_index = int(progress * len(assets)) if assets else 0
|
| 326 |
+
current_asset = assets[asset_index] if assets else None
|
| 327 |
+
|
| 328 |
+
future = executor.submit(
|
| 329 |
+
self.create_enhanced_frame,
|
| 330 |
+
current_text,
|
| 331 |
+
self.themes[style],
|
| 332 |
+
i,
|
| 333 |
+
total_frames,
|
| 334 |
+
current_asset['data'] if current_asset and current_asset['type'] == 'image' else None
|
| 335 |
+
)
|
| 336 |
+
frame_futures.append(future)
|
| 337 |
|
| 338 |
+
batch_frames = [future.result() for future in frame_futures]
|
| 339 |
+
frames.extend(batch_frames)
|
| 340 |
+
|
| 341 |
+
progress = int(60 + (batch/num_batches) * 30)
|
| 342 |
+
progress_bar.progress(progress)
|
| 343 |
+
|
| 344 |
+
# Create video clip (90%)
|
| 345 |
+
status_text.text("Compiling video...")
|
| 346 |
+
video = ImageSequenceClip(frames, fps=fps)
|
| 347 |
+
video = video.set_audio(audio)
|
| 348 |
+
progress_bar.progress(90)
|
| 349 |
+
|
| 350 |
+
# Write final video (100%)
|
| 351 |
+
status_text.text("Saving video...")
|
| 352 |
+
video.write_videofile(
|
| 353 |
+
output_path,
|
| 354 |
+
fps=fps,
|
| 355 |
+
codec='libx264',
|
| 356 |
+
audio_codec='aac',
|
| 357 |
+
threads=4,
|
| 358 |
+
preset='ultrafast' # Faster encoding
|
| 359 |
+
)
|
| 360 |
+
|
| 361 |
+
progress_bar.progress(100)
|
| 362 |
+
status_text.text("Video generation complete!")
|
| 363 |
+
return output_path
|
| 364 |
+
|
| 365 |
+
except Exception as e:
|
| 366 |
+
self.logger.error(f"Video creation failed: {str(e)}")
|
| 367 |
+
raise
|
| 368 |
+
|
| 369 |
+
def generate_visual_assets(self, script: str, style: str) -> List[Dict]:
|
| 370 |
+
"""Generate relevant visual assets based on script content"""
|
| 371 |
+
try:
|
| 372 |
+
# Simplified asset generation for faster processing
|
| 373 |
+
topics = self.extract_key_topics(script)[:3] # Limit to 3 topics
|
| 374 |
+
|
| 375 |
+
assets = []
|
| 376 |
+
for topic in topics:
|
| 377 |
+
# Create simple colored backgrounds instead of AI images
|
| 378 |
+
img = Image.new('RGB', (1920, 1080), self.themes[style]['bg'])
|
| 379 |
+
assets.append({
|
| 380 |
+
'type': 'image',
|
| 381 |
+
'data': img,
|
| 382 |
+
'topic': topic
|
| 383 |
+
})
|
| 384 |
+
|
| 385 |
+
return assets
|
| 386 |
+
|
| 387 |
+
except Exception as e:
|
| 388 |
+
self.logger.error(f"Visual asset generation failed: {str(e)}")
|
| 389 |
+
return []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 390 |
|
| 391 |
@staticmethod
|
| 392 |
def clean_text(text: str) -> str:
|