shaheerawan3 commited on
Commit
63669d5
·
verified ·
1 Parent(s): 203e15a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +157 -133
app.py CHANGED
@@ -6,7 +6,7 @@ from transformers import pipeline, AutoModelForCausalLM, AutoTokenizer
6
  from PIL import Image, ImageDraw, ImageFont
7
  import tempfile
8
  import os
9
- from moviepy.editor import AudioFileClip, concatenate_videoclips, ImageClip
10
  import numpy as np
11
  from gtts import gTTS
12
  import textwrap
@@ -457,51 +457,17 @@ class ImageScraper:
457
  return False
458
 
459
  def generate_fallback_audio(self, script: str) -> AudioFileClip:
460
- """Generate fallback audio using gTTS with improved error handling"""
461
  try:
462
  audio_path = self.temp_dir / "voice.mp3"
463
-
464
- # Split long text into chunks to avoid gTTS limitations
465
- max_chars = 5000
466
- text_chunks = [script[i:i+max_chars] for i in range(0, len(script), max_chars)]
467
-
468
- # Create temporary files for each chunk
469
- temp_files = []
470
- for i, chunk in enumerate(text_chunks):
471
- temp_path = self.temp_dir / f"voice_chunk_{i}.mp3"
472
- tts = gTTS(text=chunk, lang='en', slow=False)
473
- tts.save(str(temp_path))
474
- temp_files.append(temp_path)
475
-
476
- # Concatenate audio files if there are multiple chunks
477
- if len(temp_files) > 1:
478
- clips = [AudioFileClip(str(f)) for f in temp_files]
479
- final_clip = concatenate_audioclips(clips)
480
- final_clip.write_audiofile(str(audio_path), codec='mp3')
481
- final_clip.close()
482
- for clip in clips:
483
- clip.close()
484
- else:
485
- # Just rename the single chunk file
486
- os.rename(temp_files[0], audio_path)
487
-
488
- # Clean up temporary files
489
- for temp_file in temp_files:
490
- if temp_file.exists():
491
- temp_file.unlink()
492
-
493
- # Verify the audio file
494
- if not audio_path.exists() or audio_path.stat().st_size == 0:
495
- raise Exception("Audio file generation failed")
496
-
497
  return AudioFileClip(str(audio_path))
498
-
499
  except Exception as e:
500
- logging.error(f"Audio generation failed: {str(e)}")
501
- # Create a silent audio clip as last resort
502
- silence_duration = len(script.split()) / 3 # Approximate duration based on words
503
- return AudioFileClip(duration=silence_duration)
504
-
505
  def scrape_pexels(self, query: str) -> List[str]:
506
  urls = []
507
  try:
@@ -552,50 +518,16 @@ class EnhancedVideoGenerator:
552
 
553
 
554
  def generate_fallback_audio(self, script: str) -> AudioFileClip:
555
- """Generate fallback audio using gTTS with improved error handling"""
556
  try:
557
  audio_path = self.temp_dir / "voice.mp3"
558
-
559
- # Split long text into chunks to avoid gTTS limitations
560
- max_chars = 5000
561
- text_chunks = [script[i:i+max_chars] for i in range(0, len(script), max_chars)]
562
-
563
- # Create temporary files for each chunk
564
- temp_files = []
565
- for i, chunk in enumerate(text_chunks):
566
- temp_path = self.temp_dir / f"voice_chunk_{i}.mp3"
567
- tts = gTTS(text=chunk, lang='en', slow=False)
568
- tts.save(str(temp_path))
569
- temp_files.append(temp_path)
570
-
571
- # Concatenate audio files if there are multiple chunks
572
- if len(temp_files) > 1:
573
- clips = [AudioFileClip(str(f)) for f in temp_files]
574
- final_clip = concatenate_audioclips(clips)
575
- final_clip.write_audiofile(str(audio_path), codec='mp3')
576
- final_clip.close()
577
- for clip in clips:
578
- clip.close()
579
- else:
580
- # Just rename the single chunk file
581
- os.rename(temp_files[0], audio_path)
582
-
583
- # Clean up temporary files
584
- for temp_file in temp_files:
585
- if temp_file.exists():
586
- temp_file.unlink()
587
-
588
- # Verify the audio file
589
- if not audio_path.exists() or audio_path.stat().st_size == 0:
590
- raise Exception("Audio file generation failed")
591
-
592
  return AudioFileClip(str(audio_path))
593
-
594
  except Exception as e:
595
- logging.error(f"Audio generation failed: {str(e)}")
596
- # Create a silent audio clip as last resort
597
- silence_duration = len(script.split()) / 3 # Approximate duration based on words
598
- return AudioFileClip(duration=silence_duration)
599
 
600
  def apply_video_effects(self, frame: np.ndarray, effect_params: dict) -> np.ndarray:
601
  """Apply various video effects to a frame"""
@@ -955,60 +887,152 @@ class EnhancedVideoGenerator:
955
  msecs = int((seconds - int(seconds)) * 1000)
956
  return f"{hours:02d}:{minutes:02d}:{secs:02d},{msecs:03d}"
957
 
958
- def create_video(self, images, audio_file, duration, output_path):
959
- """Creates a video from images and audio."""
 
960
  try:
961
- if not images or not audio_file:
962
- raise ValueError("Both images and audio are required.")
963
-
964
- # Load and adjust the audio file
965
- audio = AudioFileClip(audio_file)
966
- audio = audio.subclip(0, min(duration, audio.duration)) # Adjust duration if needed
967
-
968
- # Create video clips from images
969
- video_clips = []
970
- for img_path in images:
971
- img = Image.open(img_path)
972
- clip = ImageClip(img).set_duration(duration / len(images))
973
- video_clips.append(clip)
974
-
975
- # Combine all clips and set audio
976
- final_clip = concatenate_videoclips(video_clips)
977
- final_clip = final_clip.set_audio(audio)
978
-
979
- # Write to output file
980
- final_clip.write_videofile(output_path, fps=24, codec="libx264")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
981
  return output_path
 
982
  except Exception as e:
983
- raise RuntimeError(f"Error generating video: {str(e)}")
984
-
985
- # Streamlit interface
986
- st.title("Enhanced Video Generator")
987
-
988
- # Upload files
989
- images = st.file_uploader("Upload Images", type=["jpg", "png"], accept_multiple_files=True)
990
- audio_file = st.file_uploader("Upload Audio", type=["mp3", "wav"])
991
- duration = st.number_input("Video Duration (seconds)", min_value=10, max_value=300, value=30)
992
- output_path = "output_video.mp4"
993
-
994
- # Generate video button
995
- if st.button("Generate Video"):
996
- if not images or not audio_file:
997
- st.error("Please upload both images and audio.")
998
- else:
999
- try:
1000
- generator = EnhancedVideoGenerator()
1001
- image_paths = [img.name for img in images]
1002
- for img in images:
1003
- with open(img.name, "wb") as f:
1004
- f.write(img.getbuffer())
1005
-
1006
- # Generate video
1007
- output = generator.create_video(image_paths, audio_file.name, duration, output_path)
1008
- st.success(f"Video generated: {output}")
1009
- st.video(output)
1010
- except Exception as e:
1011
- st.error(f"Error: {e}")
1012
 
1013
 
1014
 
 
6
  from PIL import Image, ImageDraw, ImageFont
7
  import tempfile
8
  import os
9
+ from moviepy.editor import *
10
  import numpy as np
11
  from gtts import gTTS
12
  import textwrap
 
457
  return False
458
 
459
  def generate_fallback_audio(self, script: str) -> AudioFileClip:
460
+ """Generate fallback audio using gTTS"""
461
  try:
462
  audio_path = self.temp_dir / "voice.mp3"
463
+ tts = gTTS(text=script, lang='en', slow=False)
464
+ tts.save(str(audio_path))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
465
  return AudioFileClip(str(audio_path))
 
466
  except Exception as e:
467
+ print(f"Fallback audio generation failed: {e}")
468
+ # Create silent audio clip
469
+ return AudioFileClip(str(audio_path)) if os.path.exists(str(audio_path)) else None
470
+
 
471
  def scrape_pexels(self, query: str) -> List[str]:
472
  urls = []
473
  try:
 
518
 
519
 
520
  def generate_fallback_audio(self, script: str) -> AudioFileClip:
521
+ """Generate fallback audio using gTTS"""
522
  try:
523
  audio_path = self.temp_dir / "voice.mp3"
524
+ tts = gTTS(text=script, lang='en', slow=False)
525
+ tts.save(str(audio_path))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
526
  return AudioFileClip(str(audio_path))
 
527
  except Exception as e:
528
+ print(f"Fallback audio generation failed: {e}")
529
+ # Create silent audio clip
530
+ return AudioFileClip(str(audio_path)) if os.path.exists(str(audio_path)) else None
 
531
 
532
  def apply_video_effects(self, frame: np.ndarray, effect_params: dict) -> np.ndarray:
533
  """Apply various video effects to a frame"""
 
887
  msecs = int((seconds - int(seconds)) * 1000)
888
  return f"{hours:02d}:{minutes:02d}:{secs:02d},{msecs:03d}"
889
 
890
+ def create_video(self, script: str, style: str, duration: int, output_path: str, selected_images: List[str],
891
+ video_effects: dict = None, progress_callback: Callable[[float], None] = None) -> str:
892
+ """Create video with selected images and effects"""
893
  try:
894
+ # Initialize default effects if none provided
895
+ if video_effects is None:
896
+ video_effects = {
897
+ 'zoom': 1.0,
898
+ 'brightness': 1.0,
899
+ 'contrast': 1.0,
900
+ 'blur': False
901
+ }
902
+
903
+ # Process images with error handling
904
+ processed_images = []
905
+ total_images = len(selected_images)
906
+
907
+ for idx, img_url in enumerate(selected_images):
908
+ try:
909
+ response = requests.get(img_url, timeout=10)
910
+ response.raise_for_status()
911
+ img = Image.open(BytesIO(response.content))
912
+ img = img.convert('RGB')
913
+ img = img.resize((1920, 1080), Image.LANCZOS)
914
+ processed_images.append(img)
915
+
916
+ # Update progress (20% of total progress is for image processing)
917
+ if progress_callback:
918
+ progress = (idx + 1) / total_images * 20
919
+ progress_callback(progress)
920
+
921
+ except Exception as e:
922
+ self.logger.error(f"Error processing image {img_url}: {e}")
923
+ continue
924
+
925
+ if not processed_images:
926
+ raise ValueError("No valid images to process")
927
+
928
+ # Generate voice-over
929
+ audio = self.generate_fallback_audio(script)
930
+ if progress_callback:
931
+ progress_callback(30) # 30% progress after audio generation
932
+
933
+ # Calculate frames
934
+ fps = 30
935
+ total_frames = int(duration * fps)
936
+ frames_per_image = total_frames // len(processed_images)
937
+
938
+ # Create frames with effects
939
+ frames = []
940
+ frame_count = 0
941
+
942
+ # Apply zoom effect over time
943
+ zoom_range = np.linspace(1.0, video_effects['zoom'], frames_per_image)
944
+
945
+ for idx, img in enumerate(processed_images):
946
+ img_array = np.array(img)
947
+
948
+ # Calculate frames for this image
949
+ if idx == len(processed_images) - 1:
950
+ n_frames = total_frames - frame_count
951
+ else:
952
+ n_frames = min(frames_per_image, total_frames - frame_count)
953
+
954
+ # Generate frames with effects
955
+ for frame_idx in range(n_frames):
956
+ current_effects = video_effects.copy()
957
+
958
+ # Update zoom factor
959
+ if video_effects['zoom'] != 1.0:
960
+ current_effects['zoom'] = zoom_range[min(frame_idx, len(zoom_range)-1)]
961
+
962
+ # Apply effects to frame
963
+ frame = self.apply_video_effects(img_array.copy(), current_effects)
964
+ frames.append(frame)
965
+ frame_count += 1
966
+
967
+ # Update progress (30% to 70% is for frame generation)
968
+ if progress_callback and frame_count % 30 == 0:
969
+ progress = 30 + (frame_count / total_frames * 40)
970
+ progress_callback(progress)
971
+
972
+ # Add transition to next image if enabled
973
+ if idx < len(processed_images) - 1 and video_effects.get('transition_style') != 'None':
974
+ next_img_array = np.array(processed_images[idx + 1])
975
+ transition_frames = 15
976
+
977
+ for t in range(transition_frames):
978
+ if frame_count < total_frames:
979
+ alpha = t / transition_frames
980
+ transition_frame = cv2.addWeighted(
981
+ img_array, 1 - alpha,
982
+ next_img_array, alpha, 0
983
+ )
984
+ frames.append(transition_frame)
985
+ frame_count += 1
986
+
987
+ # Create video clip
988
+ clip = ImageSequenceClip(frames, fps=fps)
989
+ if progress_callback:
990
+ progress_callback(80) # 80% progress after creating clip
991
+
992
+ # Adjust audio duration
993
+ if audio.duration > clip.duration:
994
+ audio = audio.subclip(0, clip.duration)
995
+ elif audio.duration < clip.duration:
996
+ clip = clip.subclip(0, audio.duration)
997
+
998
+ # Combine video and audio
999
+ final_clip = clip.set_audio(audio)
1000
+ if progress_callback:
1001
+ progress_callback(90) # 90% progress after combining audio
1002
+
1003
+ # Ensure output directory exists
1004
+ os.makedirs(os.path.dirname(output_path), exist_ok=True)
1005
+
1006
+ # Write video file
1007
+ final_clip.write_videofile(
1008
+ output_path,
1009
+ fps=fps,
1010
+ codec='libx264',
1011
+ audio_codec='aac',
1012
+ ffmpeg_params=['-pix_fmt', 'yuv420p'],
1013
+ verbose=False,
1014
+ logger=None
1015
+ )
1016
+
1017
+ if progress_callback:
1018
+ progress_callback(100) # 100% progress after writing file
1019
+
1020
  return output_path
1021
+
1022
  except Exception as e:
1023
+ self.logger.error(f"Video creation failed: {str(e)}")
1024
+ raise
1025
+ finally:
1026
+ # Cleanup
1027
+ try:
1028
+ if 'clip' in locals():
1029
+ clip.close()
1030
+ if 'final_clip' in locals():
1031
+ final_clip.close()
1032
+ if 'audio' in locals():
1033
+ audio.close()
1034
+ except Exception as e:
1035
+ self.logger.error(f"Cleanup error: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1036
 
1037
 
1038