import gradio as gr import ffmpeg import os import tempfile import subprocess import re from pathlib import Path def get_video_info(input_path): """Ambil informasi video""" try: probe = ffmpeg.probe(input_path) video_info = next(s for s in probe['streams'] if s['codec_type'] == 'video') return video_info except: return None def compress_video(input_video, quality_preset, progress=gr.Progress()): """Kompres video dengan kualitas terjaga tapi ukuran super kecil""" if input_video is None: return None, "Silakan upload video terlebih dahulu" try: progress(0, desc="Memulai proses...") # Buat file temporary untuk output output_path = tempfile.NamedTemporaryFile(delete=False, suffix='.mp4').name progress(0.1, desc="Membaca informasi video...") # Dapatkan info video video_info = get_video_info(input_video) if not video_info: return None, "Gagal membaca informasi video" original_size = os.path.getsize(input_video) / (1024 * 1024) # MB # Dapatkan durasi video duration = float(video_info.get('duration', 0)) if duration == 0: probe = ffmpeg.probe(input_video) duration = float(probe['format']['duration']) progress(0.2, desc="Menghitung parameter optimal...") # Target ukuran berdasarkan preset (dalam MB) # Naikin target size biar kualitas tetap bagus target_sizes = { "Kualitas Maksimal (ukuran lebih besar)": min(original_size * 0.4, 3.0), # Max 3MB "Seimbang (recommended)": min(original_size * 0.3, 2.0), # Max 2MB "Ukuran Kecil (kualitas masih bagus)": min(original_size * 0.25, 1.5) # Max 1.5MB } target_size_mb = target_sizes.get(quality_preset, 2.0) # Hitung target bitrate (dalam kbps) audio_bitrate = 128 # kbps - naikin lagi ke 128 target_bitrate = int((target_size_mb * 8192) / duration) - audio_bitrate target_bitrate = max(target_bitrate, 800) # Minimal 800kbps biar gak burik progress(0.25, desc="Memulai encoding video...") # Parameter x264 yang dioptimasi untuk kualitas visual x264_params = [ 'ref=5', # Lebih banyak reference frames 'bframes=4', # Lebih banyak B-frames 'b-adapt=2', 'direct=auto', 'me=umh', 'subme=10', # Naikin subpixel ME untuk detail lebih baik 'trellis=2', 'aq-mode=3', 'aq-strength=1.0', # Naikin AQ strength 'psy-rd=1.0:0.2', # Lebih fokus ke psychovisual 'deblock=0:0', # Deblocking normal biar gak burik 'fast-pskip=0', # Matiin fast skip biar lebih detail 'mixed-refs=1', 'rc-lookahead=60', # Lebih banyak lookahead 'mbtree=1', 'qcomp=0.6', # Quantizer compression 'qpmin=10', # Minimal QP biar gak terlalu compress 'qpmax=51', ] # Build command ffmpeg cmd = [ 'ffmpeg', '-i', input_video, '-vcodec', 'libx264', '-b:v', f'{target_bitrate}k', '-maxrate', f'{int(target_bitrate * 1.3)}k', # Turunin maxrate multiplier '-bufsize', f'{int(target_bitrate * 2)}k', '-preset', 'slow', # Ganti ke slow (veryslow terlalu lama) '-profile:v', 'high', # Profile high untuk kualitas lebih baik '-level', '4.1', '-acodec', 'aac', '-b:a', f'{audio_bitrate}k', '-pix_fmt', 'yuv420p', '-movflags', 'faststart', '-x264-params', ':'.join(x264_params), '-y', output_path ] # Jalankan ffmpeg dengan progress tracking process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True ) # Track progress dari stderr ffmpeg for line in process.stderr: # Parse time dari output ffmpeg time_match = re.search(r'time=(\d{2}):(\d{2}):(\d{2}\.\d{2})', line) if time_match: hours = int(time_match.group(1)) minutes = int(time_match.group(2)) seconds = float(time_match.group(3)) current_time = hours * 3600 + minutes * 60 + seconds # Hitung progress (25% sampai 95%) if duration > 0: encode_progress = min(current_time / duration, 1.0) total_progress = 0.25 + (encode_progress * 0.7) progress(total_progress, desc=f"Encoding video... {int(encode_progress * 100)}%") process.wait() if process.returncode != 0: raise Exception("FFmpeg encoding gagal") progress(0.95, desc="Finalisasi...") # Hitung hasil kompresi compressed_size = os.path.getsize(output_path) / (1024 * 1024) reduction = ((original_size - compressed_size) / original_size) * 100 progress(1.0, desc="Selesai!") result_text = f""" ✅ Konversi berhasil! 📊 Hasil: • Ukuran awal: {original_size:.2f} MB • Ukuran akhir: {compressed_size:.2f} MB • Pengurangan: {reduction:.1f}% • Target bitrate: {target_bitrate} kbps • Codec: H.264 (optimized) Video sudah dikompres maksimal dengan tetap menjaga kualitas visual HD. """ return output_path, result_text except Exception as e: error_msg = str(e) return None, f"Terjadi kesalahan: {error_msg}" # Buat interface with gr.Blocks() as demo: gr.Markdown(""" # Video Optimizer Kompres video kamu tanpa mengurangi kualitas. Cocok untuk kirim via WhatsApp atau media sosial lainnya. """) with gr.Row(): with gr.Column(): input_video = gr.Video(label="Upload Video") quality_preset = gr.Radio( choices=[ "Kualitas Maksimal (ukuran lebih besar)", "Seimbang (recommended)", "Ukuran Kecil (kualitas masih bagus)" ], value="Seimbang (recommended)", label="Pilih Preset" ) compress_btn = gr.Button("Proses Video", variant="primary") with gr.Column(): output_video = gr.Video(label="Video Hasil") result_text = gr.Textbox(label="Status", lines=8) compress_btn.click( fn=compress_video, inputs=[input_video, quality_preset], outputs=[output_video, result_text] ) gr.Markdown(""" ### Tips: - Gunakan preset "Seimbang" untuk hasil optimal - Video akan dikonversi ke format H.265 yang lebih efisien - Kualitas warna dan detail akan tetap terjaga """) if __name__ == "__main__": demo.queue() demo.launch(server_name="0.0.0.0", server_port=7860, share=False)