Download app.py from Kompres/wa: direct link, hf CLI and curl.
- Browser
- Download file 7.47 kB
-
https://huggingface.co/spaces/Kompres/wa/resolve/main/app.py
- Command line
-
hf download hf://spaces/Kompres/wa/app.py
-
curl -L -o app.py https://huggingface.co/spaces/Kompres/wa/resolve/main/app.py
7.47 kB
| 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) | |