Ytstock / app.py
Artificial-superintelligence's picture
Update app.py
716c1e6 verified
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
)