Spaces:
Sleeping
Sleeping
File size: 3,555 Bytes
fe344af 1ef629d c1d18ca fe344af 5b1e631 9ef83c3 fe344af 9ef83c3 c1d18ca fe344af 5b1e631 9ef83c3 5b1e631 fe344af 9ef83c3 79cd2d8 9ef83c3 79cd2d8 9ef83c3 5b1e631 9ef83c3 79cd2d8 9ef83c3 5b1e631 9ef83c3 79cd2d8 5b1e631 9ef83c3 5b1e631 9ef83c3 5b1e631 fe344af 9ef83c3 1ef629d c1d18ca fe344af 9ef83c3 5b1e631 c1d18ca fe344af 9ef83c3 5b1e631 fe344af c1d18ca fe344af 5b1e631 c1d18ca fe344af 5b1e631 c1d18ca 9ef83c3 c1d18ca fe344af 5b1e631 9ef83c3 c1d18ca 9ef83c3 187d44e c1d18ca 9ef83c3 fe344af c1d18ca |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
import gradio as gr
import subprocess
import os
from moviepy.editor import VideoFileClip
import whisper
import tempfile
import shutil
import yt_dlp
# Load Whisper model
model = whisper.load_model("small")
def process_video(video):
"""
Handles:
- Gradio uploads
- n8n binary uploads (multipart/form-data)
- YouTube or web URLs
"""
try:
temp_dir = tempfile.mkdtemp()
# Handle video input from various sources
if isinstance(video, str) and video.startswith(("http://", "https://")):
# π₯ If it's a YouTube link or direct URL, download it
video_path = os.path.join(temp_dir, "downloaded_video.mp4")
ydl_opts = {
"format": "bestvideo+bestaudio/best",
"outtmpl": video_path,
"quiet": True,
"merge_output_format": "mp4"
}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
ydl.download([video])
elif isinstance(video, dict) and "name" in video:
# π¦ n8n file input (dict)
video_path = video["name"]
elif hasattr(video, "name"):
# π Uploaded from Gradio UI
video_path = os.path.join(temp_dir, os.path.basename(video.name))
shutil.copy(video.name, video_path)
elif isinstance(video, str) and os.path.exists(video):
# π Local file path
video_path = video
else:
raise ValueError("Invalid video input type")
# π§ Extract audio
video_clip = VideoFileClip(video_path)
audio_path = os.path.join(temp_dir, "audio.wav")
video_clip.audio.write_audiofile(audio_path, codec="pcm_s16le")
# π£ Transcribe using Whisper
result = model.transcribe(audio_path, language="en")
transcript = result["text"]
# π¬ Save transcript to SRT
srt_path = os.path.join(temp_dir, "transcript.srt")
with open(srt_path, "w", encoding="utf-8") as f:
for i, seg in enumerate(result["segments"]):
start = seg["start"]
end = seg["end"]
text = seg["text"].strip()
f.write(f"{i+1}\n{format_time(start)} --> {format_time(end)}\n{text}\n\n")
# π₯ Burn subtitles into video
output_path = os.path.join(temp_dir, "output_with_subs.mp4")
burn_subtitles(video_path, srt_path, output_path)
return output_path, transcript
except Exception as e:
return None, f"β Error: {str(e)}"
def format_time(seconds):
hrs, rem = divmod(seconds, 3600)
mins, secs = divmod(rem, 60)
millis = int((secs - int(secs)) * 1000)
return f"{int(hrs):02}:{int(mins):02}:{int(secs):02},{millis:03}"
def burn_subtitles(video_path, srt_path, output_path):
"""Use FFmpeg to burn subtitles permanently into the video."""
cmd = [
"ffmpeg", "-y",
"-i", video_path,
"-vf", f"subtitles={srt_path}",
"-c:a", "copy",
output_path
]
subprocess.run(cmd, check=True)
# ποΈ Gradio Interface
iface = gr.Interface(
fn=process_video,
inputs=gr.Textbox(label="Enter YouTube URL or Upload Video File"),
outputs=[
gr.Video(label="Video with Subtitles"),
gr.Textbox(label="Transcript", lines=8)
],
title="π¬ Whisper Subtitle Burner",
description="Upload a video or paste a YouTube link. This app generates English subtitles using Whisper and burns them into the video."
)
iface.launch()
|