File size: 4,758 Bytes
5e77470 | 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 113 114 | import whisper
import os
import subprocess
import gradio as gr
import shutil
def process_video_source(num_clips, duration, mode, url, uploaded_file, progress=gr.Progress()):
try:
progress(0, desc="Initializing...")
output_template = "source_video.mp4"
# Determine Source: File Upload takes priority over URL
if uploaded_file is not None:
progress(0.1, desc="Processing uploaded file...")
if os.path.exists(output_template):
os.remove(output_template)
shutil.copy(uploaded_file.name, output_template)
video_path = output_template
elif url:
progress(0.1, desc="Downloading from YouTube...")
video_path = download_youtube(url)
else:
return "β Error: Please provide either a YouTube URL or upload a video file.", []
if video_path is None or not os.path.exists(video_path):
return "β Source acquisition failed. Check URL or File.", []
# 2. Transcribe
progress(0.3, desc="Analyzing Audio (Whisper)...")
model = whisper.load_model("base")
result = model.transcribe(video_path, fp16=False)
# 3. Logic
progress(0.6, desc="Finding best segments...")
best_parts = get_best_segments(result['segments'], int(num_clips), int(duration))
output_dir = f"clips_{mode}"
if os.path.exists(output_dir):
shutil.rmtree(output_dir)
os.makedirs(output_dir)
# 4. Export
clip_paths = []
for i, clip in enumerate(best_parts):
progress(0.6 + (0.3 * (i/len(best_parts))), desc=f"Exporting Clip {i+1}...")
output_name = os.path.abspath(f"{output_dir}/clip_{i+1}.mp4")
start_time = clip['start']
if mode.lower() == "short":
vf_filter = "crop=ih*(9/16):ih"
cmd = f"ffmpeg -ss {start_time} -t {int(duration)} -i {video_path} -vf '{vf_filter}' -c:v libx264 -crf 23 -c:a aac -y {output_name}"
else:
cmd = f"ffmpeg -ss {start_time} -t {int(duration)} -i {video_path} -c:v libx264 -c:a aac -y {output_name}"
subprocess.run(cmd, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
clip_paths.append(output_name)
progress(1.0, desc="Done!")
return f"β
Success! Created {len(clip_paths)} clips.", clip_paths
except Exception as e:
return f"β Error: {str(e)}", []
def download_youtube(url):
output_template = "source_video.mp4"
cookie_arg = "--cookies cookies.txt" if os.path.exists("cookies.txt") else ""
cmd = f'yt-dlp {cookie_arg} -f "bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best" --no-check-certificate -o "{output_template}" "{url}"'
result = subprocess.run(cmd, shell=True)
return output_template if result.returncode == 0 else None
def get_best_segments(segments, num_clips, duration):
scored_segments = []
for s in segments:
score = len(s['text'].split()) / (s['end'] - s['start'] + 0.1)
scored_segments.append({'start': s['start'], 'score': score})
scored_segments.sort(key=lambda x: x['score'], reverse=True)
final = []
for s in scored_segments:
if len(final) >= int(num_clips): break
if not any(abs(s['start'] - f['start']) < int(duration) for f in final):
final.append(s)
return final
# --- Gradio Interface ---
with gr.Blocks(title="Bulk Viral Video Generator") as demo:
gr.Markdown("# π₯ Bulk Viral Video Generator")
with gr.Row():
with gr.Column():
yt_url = gr.Textbox(label="YouTube URL (Optional if uploading file)", placeholder="Enter URL here...")
file_input = gr.File(label="OR Upload Video File", file_types=["video"])
with gr.Row():
num_shorts = gr.Number(label="Number of Shorts", value=5, precision=0)
duration = gr.Number(label="Seconds per Clip", value=60, precision=0)
mode = gr.Radio(choices=["short", "long"], label="Format", value="short")
btn = gr.Button("π Generate Viral Clips", variant="primary")
with gr.Column():
status = gr.Textbox(label="Status")
gallery = gr.Gallery(label="Generated Clips", columns=2, height="auto")
btn.click(
fn=process_video_source,
inputs=[num_shorts, duration, mode, yt_url, file_input],
outputs=[status, gallery]
)
if __name__ == "__main__":
# Listening on 0.0.0.0 for local network/docker access
demo.launch(server_name="0.0.0.0", server_port=7860, share=False)
|