|
import gradio as gr |
|
import yt_dlp |
|
import os |
|
import re |
|
import time |
|
from moviepy.video.io.VideoFileClip import VideoFileClip |
|
|
|
|
|
def sanitize_filename(title): |
|
return re.sub(r'[<>:"/\\|?*]', '_', title) |
|
|
|
|
|
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', |
|
'merge_output_format': 'mp4', |
|
'max_filesize': 100 * 1024 * 1024, |
|
} |
|
|
|
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 |
|
|
|
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 |
|
|
|
|
|
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)) |
|
|
|
|
|
if end_time - start_time > 60: |
|
end_time = start_time + 60 |
|
|
|
cropped_video = video.subclip(start_time, end_time) |
|
|
|
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 |
|
|
|
|
|
def process_video(url, start_time, end_time): |
|
try: |
|
if not url or not url.strip(): |
|
return gr.File.update(value=None, visible=True) |
|
|
|
|
|
if not url.startswith(('http://', 'https://')): |
|
return gr.File.update(value=None, visible=True) |
|
|
|
|
|
video_path = download_video(url) |
|
if not video_path: |
|
return gr.File.update(value=None, visible=True) |
|
|
|
|
|
cropped_path = crop_video(video_path, float(start_time), float(end_time)) |
|
if not cropped_path: |
|
return gr.File.update(value=None, visible=True) |
|
|
|
|
|
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) |
|
|
|
|
|
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 |
|
) |
|
end_time_input = gr.Number( |
|
label="End Time (seconds)", |
|
value=10, |
|
minimum=1, |
|
maximum=3600 |
|
) |
|
|
|
with gr.Row(): |
|
process_btn = gr.Button("Download and Crop Video") |
|
|
|
with gr.Row(): |
|
output_file = gr.File(label="Cropped Video", visible=True) |
|
|
|
|
|
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 |
|
|
|
|
|
if __name__ == "__main__": |
|
demo = create_interface() |
|
demo.queue() |
|
demo.launch( |
|
share=False, |
|
debug=True, |
|
server_name="0.0.0.0", |
|
server_port=7860, |
|
max_threads=3 |
|
) |