import gradio as gr import yt_dlp import os import re import time from moviepy.video.io.VideoFileClip import VideoFileClip # Function to sanitize the filename def sanitize_filename(title): return re.sub(r'[<>:"/\\|?*]', '_', title) # Function to download video and return the file path def download_video(url): download_path = '/tmp/downloads' if not os.path.exists(download_path): os.makedirs(download_path) ydl_opts = { 'outtmpl': os.path.join(download_path, '%(title)s.%(ext)s'), 'format': 'best[ext=mp4]/best', # Simplified format selection 'merge_output_format': 'mp4', 'max_filesize': 100 * 1024 * 1024, # 100MB limit } try: with yt_dlp.YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info(url, download=True) video_path = ydl.prepare_filename(info) if os.path.exists(video_path): return video_path # If the file extension is different from mp4, look for the actual file possible_path = video_path.rsplit('.', 1)[0] + '.mp4' return possible_path if os.path.exists(possible_path) else None except Exception as e: print(f"Download error: {str(e)}") return None # Function to crop the video def crop_video(input_path, start_time, end_time): if not input_path or not os.path.exists(input_path): return None output_path = os.path.join('/tmp/downloads', f'cropped_{int(time.time())}.mp4') try: with VideoFileClip(input_path) as video: duration = video.duration end_time = min(end_time, duration) start_time = max(0, min(start_time, end_time - 1)) # Limit output duration to 60 seconds if end_time - start_time > 60: end_time = start_time + 60 cropped_video = video.subclip(start_time, end_time) # Use lower quality settings for faster processing cropped_video.write_videofile( output_path, codec='libx264', audio_codec='aac', preset='ultrafast', bitrate='1000k' ) return output_path if os.path.exists(output_path) else None except Exception as e: print(f"Cropping error: {str(e)}") return None # Main processing function def process_video(url, start_time, end_time): try: if not url or not url.strip(): return gr.File.update(value=None, visible=True) # Add basic URL validation if not url.startswith(('http://', 'https://')): return gr.File.update(value=None, visible=True) # Download video video_path = download_video(url) if not video_path: return gr.File.update(value=None, visible=True) # Crop video cropped_path = crop_video(video_path, float(start_time), float(end_time)) if not cropped_path: return gr.File.update(value=None, visible=True) # Clean up original video try: os.remove(video_path) except: pass return gr.File.update(value=cropped_path, visible=True) except Exception as e: print(f"Processing error: {str(e)}") return gr.File.update(value=None, visible=True) # Create Gradio interface def create_interface(): with gr.Blocks() as demo: gr.Markdown("# YouTube Video Downloader and Cropper") with gr.Row(): url_input = gr.Textbox( label="YouTube URL", placeholder="Enter YouTube video URL here...", max_lines=1 ) with gr.Row(): start_time_input = gr.Number( label="Start Time (seconds)", value=0, minimum=0, maximum=3600 # 1 hour max ) end_time_input = gr.Number( label="End Time (seconds)", value=10, minimum=1, maximum=3600 # 1 hour max ) with gr.Row(): process_btn = gr.Button("Download and Crop Video") with gr.Row(): output_file = gr.File(label="Cropped Video", visible=True) # Add error message component error_message = gr.Markdown(visible=False) def process_with_error_handling(*args): try: return process_video(*args) except Exception as e: return gr.File.update(value=None, visible=True) process_btn.click( fn=process_with_error_handling, inputs=[url_input, start_time_input, end_time_input], outputs=[output_file] ) return demo # Launch the app if __name__ == "__main__": demo = create_interface() demo.queue() # Enable queuing demo.launch( share=False, debug=True, server_name="0.0.0.0", server_port=7860, max_threads=3 )