import gradio as gr import requests import subprocess import os import uuid import re # Fungsi untuk mengekstrak ID Video (misal: I0RkuBBi0a8) dari URL lengkap def extract_video_id(url): pattern = r"(?:v=|\/)([0-9A-Za-z_-]{11}).*" match = re.search(pattern, url) return match.group(1) if match else None def get_direct_url(youtube_url): video_id = extract_video_id(youtube_url) if not video_id: return None, "Format URL YouTube tidak valid." # Menggunakan beberapa instance Piped API sebagai Proxy Load Balancer api_urls = [ f"https://pipedapi.kavin.rocks/streams/{video_id}", f"https://pipedapi.adminforge.de/streams/{video_id}", f"https://api.piped.projectsegfau.lt/streams/{video_id}" ] for api_url in api_urls: try: response = requests.get(api_url, timeout=15) if response.status_code == 200: data = response.json() streams = data.get("videoStreams", []) # Cari stream yang berisi gabungan video+audio (progressive) # Piped menandai stream tanpa audio sebagai videoOnly: true progressive_streams = [s for s in streams if not s.get("videoOnly", True)] if progressive_streams: # Ambil kualitas yang tersedia (biasanya sudah dibungkus proxy Piped) return progressive_streams[0]["url"], f"Sukses via {api_url.split('/')[2]}" except Exception: # Jika server Piped ini mati/timeout, abaikan dan coba server berikutnya continue return None, "Semua server proxy Piped sedang sibuk atau gagal merespons." def clip_youtube(url, start_time, end_time): # 1. Dapatkan Direct Link dari Piped direct_url, extract_status = get_direct_url(url) if not direct_url: return None, f"Gagal membobol link. Detail: {extract_status}" # 2. Persiapan File output_filename = f"clip_{uuid.uuid4().hex[:8]}.mp4" start_sec = parse_time(start_time) end_sec = parse_time(end_time) duration = end_sec - start_sec if duration <= 0: return None, "Waktu selesai harus lebih besar dari waktu mulai!" # 3. Eksekusi FFmpeg langsung dari URL Streaming Proxy cmd = [ "ffmpeg", "-ss", str(start_sec), "-i", direct_url, "-t", str(duration), "-c:v", "libx264", "-preset", "veryfast", "-c:a", "aac", "-y", output_filename ] try: subprocess.run(cmd, check=True, capture_output=True) if os.path.exists(output_filename): return output_filename, "Berhasil! Klip siap diputar dan diunduh." else: return None, "Terjadi kesalahan saat memotong video di server." except subprocess.CalledProcessError as e: return None, f"Error FFmpeg: {e.stderr.decode('utf-8')}" # Helper function def parse_time(time_str): try: parts = time_str.split(':') if len(parts) == 2: return int(parts[0]) * 60 + int(parts[1]) elif len(parts) == 3: return int(parts[0]) * 3600 + int(parts[1]) * 60 + int(parts[2]) return int(time_str) except: return 0 # UI Gradio with gr.Blocks(title="YouTube Piped Clipper") as app: gr.Markdown("## ✂️ YouTube Clipper (Proxy Version)") gr.Markdown("Mengekstrak dan memotong video menggunakan jaringan proxy *open-source* (Piped) untuk menghindari blokir infrastruktur YouTube.") with gr.Row(): with gr.Column(): url_input = gr.Textbox(label="URL YouTube", placeholder="https://www.youtube.com/watch?v=...") start_input = gr.Textbox(label="Waktu Mulai (MM:SS)", placeholder="01:30") end_input = gr.Textbox(label="Waktu Selesai (MM:SS)", placeholder="02:00") submit_btn = gr.Button("✂️ Potong Video", variant="primary") with gr.Column(): video_output = gr.Video(label="Hasil Klip") status_output = gr.Textbox(label="Status Sistem", interactive=False) submit_btn.click( fn=clip_youtube, inputs=[url_input, start_input, end_input], outputs=[video_output, status_output] ) app.queue(max_size=5).launch()