Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| from moviepy.editor import VideoFileClip, VideoClip | |
| from moviepy.video.fx.all import crop | |
| import tempfile | |
| import os | |
| import numpy as np | |
| from PIL import Image, ImageDraw | |
| import hashlib | |
| # Fix for PIL.Image.ANTIALIAS deprecation | |
| if not hasattr(Image, 'ANTIALIAS'): | |
| Image.ANTIALIAS = Image.LANCZOS | |
| # Function to create a circular mask | |
| def create_circular_mask(size): | |
| # Create a square image with a black background | |
| img = Image.new('L', (size, size), 0) | |
| # Create a white circle in the middle | |
| draw = ImageDraw.Draw(img) | |
| draw.ellipse((0, 0, size, size), fill=255) | |
| # Convert the image to a numpy array | |
| mask = np.array(img) / 255.0 # Normalize to [0, 1] range | |
| return mask | |
| def compute_file_hash(file): | |
| hash_md5 = hashlib.md5() | |
| for chunk in file: | |
| hash_md5.update(chunk) | |
| file.seek(0) # Reset file pointer after reading | |
| return hash_md5.hexdigest() | |
| # Function to process the video with audio and progress logging | |
| # Ignore `input_path` by setting its hash to None | |
| def process_video(input_path, file_hash): | |
| # Load the video file | |
| input_video = VideoFileClip(input_path) | |
| w, h = input_video.size | |
| circle_size = 360 | |
| aspect_ratio = w / h | |
| # Resize the video while maintaining aspect ratio | |
| if w > h: | |
| new_w = int(circle_size * aspect_ratio) | |
| new_h = circle_size | |
| else: | |
| new_w = circle_size | |
| new_h = int(circle_size / aspect_ratio) | |
| # Resize and crop the video to a square | |
| resized_video = input_video.resize((new_w, new_h)) | |
| square_video = crop( | |
| resized_video, | |
| x_center=new_w / 2, | |
| y_center=new_h / 2, | |
| width=circle_size, | |
| height=circle_size | |
| ) | |
| # Create a circular mask | |
| mask_array = create_circular_mask(circle_size) | |
| # Apply the circular mask to each frame | |
| def apply_mask(get_frame, t): | |
| frame = get_frame(t) | |
| alpha = mask_array[..., np.newaxis] # Add axis for channels | |
| frame = frame * alpha + 255 * (1 - alpha) # Assuming white background | |
| return frame.astype('uint8') | |
| circular_video = square_video.fl(apply_mask) | |
| # Add audio back to the video | |
| circular_video = circular_video.set_audio(input_video.audio) | |
| # Save output to a temporary file with progress logging | |
| temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") | |
| circular_video.write_videofile( | |
| temp_file.name, | |
| codec="libx264", | |
| audio_codec="aac", | |
| temp_audiofile=tempfile.NamedTemporaryFile(suffix=".m4a").name, | |
| remove_temp=True, | |
| ) | |
| return temp_file.name | |
| # Streamlit UI | |
| st.title("Circular Video Cropper with Progress") | |
| st.write("Upload a video, and the app will crop it into a circular format with visible progress.") | |
| # Upload file | |
| uploaded_file = st.file_uploader("Choose a video file", type=["mp4", "mov", "avi", "mkv"]) | |
| if uploaded_file: | |
| file_hash = compute_file_hash(uploaded_file) | |
| # Save uploaded file to a temporary location | |
| temp_input_file = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") | |
| temp_input_file.write(uploaded_file.read()) | |
| temp_input_file.close() | |
| input_video = VideoFileClip(temp_input_file.name) | |
| total_duration = input_video.duration | |
| output_path = process_video(temp_input_file.name, file_hash) | |
| # Display success and download button | |
| st.success("Video processed successfully!") | |
| with open(output_path, "rb") as file: | |
| st.download_button( | |
| label="Download Circular Video", | |
| data=file, | |
| file_name="circular_video.mp4", | |
| mime="video/mp4" | |
| ) | |
| os.remove(temp_input_file.name) | |