import streamlit as st import os import tempfile import numpy as np import cv2 from PIL import Image import subprocess import random import time # Set page config st.set_page_config(page_title="Audio + Image to Video Converter", layout="wide") def add_snow_effect(frame, density=0.01): """Add snow effect to a frame""" h, w = frame.shape[:2] snow_points = np.random.rand(int(h*w*density), 2) snow_points[:, 0] *= h snow_points[:, 1] *= w for y, x in snow_points.astype(int): if 0 <= y < h and 0 <= x < w: frame[y, x] = [255, 255, 255] # White snow return frame def add_heart_effect(frame, density=0.002): """Add heart effect to a frame""" h, w = frame.shape[:2] heart_points = np.random.rand(int(h*w*density), 2) heart_points[:, 0] *= h heart_points[:, 1] *= w heart_colors = [ [255, 0, 0], # Red [255, 20, 147], # Pink [255, 105, 180] # Hot Pink ] for y, x in heart_points.astype(int): if 0 <= y < h and 0 <= x < w: color = random.choice(heart_colors) cv2.drawMarker(frame, (int(x), int(y)), color, markerType=cv2.MARKER_STAR, markerSize=10, thickness=2) return frame def add_wave_effect(frame, time_val, amplitude=20, frequency=0.1): """Add wave effect to a frame""" h, w = frame.shape[:2] for x in range(w): offset = int(amplitude * np.sin(frequency * x + time_val)) for y in range(h): src_y = y + offset if 0 <= src_y < h: frame[y, x] = frame[src_y, x] return frame def add_sparkle_effect(frame, density=0.001): """Add sparkle effect to a frame""" h, w = frame.shape[:2] sparkle_points = np.random.rand(int(h*w*density), 2) sparkle_points[:, 0] *= h sparkle_points[:, 1] *= w sparkle_colors = [ [255, 215, 0], # Gold [255, 255, 255], # White [173, 216, 230] # Light Blue ] for y, x in sparkle_points.astype(int): if 0 <= y < h and 0 <= x < w: size = random.randint(1, 3) color = random.choice(sparkle_colors) cv2.circle(frame, (int(x), int(y)), size, color, -1) return frame def get_audio_duration(audio_path): """Get the duration of an audio file using ffprobe""" cmd = [ 'ffprobe', '-v', 'error', '-show_entries', 'format=duration', '-of', 'default=noprint_wrappers=1:nokey=1', audio_path ] result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) return float(result.stdout.strip()) def create_video(image_path, audio_path, effect_type, output_path, fps=24): """Create a video from an image and audio with the selected effect""" # Get audio duration try: duration = get_audio_duration(audio_path) except Exception as e: st.error(f"Error getting audio duration: {e}") # Fallback to a default duration if ffprobe fails duration = 30.0 # Load image img = cv2.imread(image_path) h, w = img.shape[:2] # Define output video parameters fourcc = cv2.VideoWriter_fourcc(*'mp4v') temp_video_path = tempfile.NamedTemporaryFile(suffix='.mp4', delete=False).name out = cv2.VideoWriter(temp_video_path, fourcc, fps, (w, h)) # Calculate total frames total_frames = int(duration * fps) progress_bar = st.progress(0) # Generate video frames with effect for i in range(total_frames): progress = i / total_frames progress_bar.progress(progress) # Create a copy of the original image frame = img.copy() time_val = i / fps # Apply selected effect if effect_type == 'Snow': frame = add_snow_effect(frame) elif effect_type == 'Hearts': frame = add_heart_effect(frame) elif effect_type == 'Waves': frame = add_wave_effect(frame, time_val) elif effect_type == 'Sparkles': frame = add_sparkle_effect(frame) elif effect_type == 'Random': effect = random.choice(['Snow', 'Hearts', 'Waves', 'Sparkles']) if effect == 'Snow': frame = add_snow_effect(frame) elif effect == 'Hearts': frame = add_heart_effect(frame) elif effect == 'Waves': frame = add_wave_effect(frame, time_val) elif effect == 'Sparkles': frame = add_sparkle_effect(frame) # Write the frame out.write(frame) # Release the video writer out.release() # Combine the silent video with audio using ffmpeg directly cmd = [ 'ffmpeg', '-i', temp_video_path, # Input video file '-i', audio_path, # Input audio file '-map', '0:v', # Use video from first input '-map', '1:a', # Use audio from second input '-c:v', 'copy', # Copy the video codec '-c:a', 'aac', # Use AAC for audio '-shortest', # End when the shortest input ends output_path # Output file ] # Run ffmpeg command subprocess.run(cmd, check=True) # Clean up temporary file os.remove(temp_video_path) progress_bar.progress(1.0) return output_path def main(): st.title("🎵 Audio + Image to Video Creator 🖼️") st.markdown(""" Upload an audio file and an image to create a video! The image will be static throughout the video, and you can add visual effects. """) # Sidebar for effects selection st.sidebar.title("Effect Options") effect_type = st.sidebar.selectbox( "Choose Visual Effect", ["None", "Snow", "Hearts", "Waves", "Sparkles", "Random"] ) # File uploaders col1, col2 = st.columns(2) with col1: st.subheader("Upload Audio File") audio_file = st.file_uploader("Choose an audio file", type=["mp3", "wav", "ogg"]) if audio_file is not None: # Save the audio file temporarily audio_path = tempfile.NamedTemporaryFile(delete=False, suffix='.mp3').name with open(audio_path, "wb") as f: f.write(audio_file.getbuffer()) st.audio(audio_file) with col2: st.subheader("Upload Image") image_file = st.file_uploader("Choose an image", type=["jpg", "jpeg", "png"]) if image_file is not None: # Save the image file temporarily image_path = tempfile.NamedTemporaryFile(delete=False, suffix='.jpg').name with open(image_path, "wb") as f: f.write(image_file.getbuffer()) st.image(image_file, caption="Uploaded Image", use_column_width=True) # Generate video when both files are uploaded if st.button("Create Video", disabled=(audio_file is None or image_file is None)): if audio_file is not None and image_file is not None: st.subheader("Creating Video...") # Create a temporary output file output_path = tempfile.NamedTemporaryFile(delete=False, suffix='.mp4').name with st.spinner("Processing..."): try: video_path = create_video(image_path, audio_path, effect_type, output_path) # Display the output video st.subheader("Generated Video") st.video(video_path) # Provide download link with open(video_path, "rb") as file: btn = st.download_button( label="Download Video", data=file, file_name="output_video.mp4", mime="video/mp4" ) except Exception as e: st.error(f"Error creating video: {e}") finally: # Clean up temporary files if 'audio_path' in locals(): os.remove(audio_path) if 'image_path' in locals(): os.remove(image_path) if 'output_path' in locals() and os.path.exists(output_path): os.remove(output_path) if __name__ == "__main__": main()