import gradio as gr import os import uuid from pydub import AudioSegment from pydub.silence import split_on_silence import re import click # --- LOGIC SECTION (SAME) --- def clean_file_name(file_path): file_name = os.path.basename(file_path) file_name, file_extension = os.path.splitext(file_name) cleaned = re.sub(r'[^a-zA-Z\d]+', '_', file_name) clean_file_name = re.sub(r'_+', '_', cleaned).strip('_') random_uuid = uuid.uuid4().hex[:6] clean_file_path = os.path.join(os.path.dirname(file_path), clean_file_name + f"_{random_uuid}" + file_extension) return clean_file_path def remove_silence(file_path, minimum_silence=50): sound = AudioSegment.from_file(file_path) audio_chunks = split_on_silence(sound, min_silence_len=100, silence_thresh=-45, keep_silence=minimum_silence) combined = AudioSegment.empty() for chunk in audio_chunks: combined += chunk output_path = clean_file_name(file_path) combined.export(output_path) return output_path def calculate_duration(file_path): audio = AudioSegment.from_file(file_path) return len(audio) / 1000.0 def process_audio(audio_file, seconds=0.05): if not audio_file: return None, None, "⚠️ Please upload a file first." keep_silence = int(seconds * 1000) output_audio_file = remove_silence(audio_file, minimum_silence=keep_silence) before = calculate_duration(audio_file) after = calculate_duration(output_audio_file) text = f"⏱️ Old: {before:.2f}s | ✅ New: {after:.2f}s | 🚀 Saved: {before-after:.2f}s" return output_audio_file, output_audio_file, text def reset_interface(): return None, 0.05, None, None, "" # --- NEW MOBILE-OPTIMIZED UI --- def ui(): # Clean Modern Font Theme theme = gr.themes.Soft( primary_hue="cyan", secondary_hue="slate", font=[gr.themes.GoogleFont("Poppins"), "sans-serif"], # Changed to Poppins (Cleaner) ).set( body_background_fill="#050505", block_background_fill="#121212", block_border_width="0px", input_background_fill="#1a1a1a" ) css = """ /* MOBILE FIX: Container width 100% karega */ .gradio-container { width: 100% !important; max-width: 100% !important; padding: 10px !important; margin: 0 !important; overflow-x: hidden; } /* Font Change */ * { font-family: 'Poppins', sans-serif !important; } /* RGB Animation */ @keyframes rgb-flow { 0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } } /* Title Styling - Mobile Friendly */ .rgb-text { background: linear-gradient(45deg, #ff0000, #00ffd5, #002bff, #ff00c8); background-size: 300%; -webkit-background-clip: text; -webkit-text-fill-color: transparent; font-weight: 800; font-size: 2rem; /* Thoda chota kiya mobile ke liye */ text-align: center; animation: rgb-flow 8s linear infinite; margin-bottom: 5px; line-height: 1.2; } .subtitle { text-align: center; color: #888; font-size: 0.9rem; margin-bottom: 15px; } /* Card Styling */ .rgb-card { background: #111; border-radius: 15px; padding: 2px; /* Border patla kiya */ margin-bottom: 10px; position: relative; } .rgb-card::before { content: ""; position: absolute; inset: 0; z-index: -1; background: linear-gradient(45deg, #ff0000, #00ffd5, #002bff, #ff00c8); background-size: 400%; border-radius: 15px; animation: rgb-flow 15s linear infinite; opacity: 0.6; } .inner-card { background: #0a0a0a; border-radius: 13px; padding: 15px; } /* Button Styling */ .process-btn { background: linear-gradient(90deg, #00c6ff 0%, #0072ff 100%) !important; color: white !important; font-weight: 600 !important; border: none !important; box-shadow: 0 4px 15px rgba(0, 114, 255, 0.3); } .reset-btn { background: #ff0055 !important; color: white !important; border: none !important; } /* Stats Box Text Fix */ .stat-box textarea { background-color: #000 !important; color: #00ff88 !important; font-family: monospace; font-size: 14px !important; } /* Mobile Specific Tweaks */ @media (max-width: 768px) { .rgb-text { font-size: 1.5rem !important; } /* Mobile pe title chota */ .inner-card { padding: 10px !important; } .process-btn, .reset-btn { width: 100%; margin-top: 5px; } /* Buttons full width */ } """ with gr.Blocks(theme=theme, css=css, title="Silence Remover Pro") as demo: gr.HTML("""
SILENCE REMOVER
Clean Audio • Fast • AI Powered
""") with gr.Group(elem_classes="rgb-card"): with gr.Column(elem_classes="inner-card"): # Mobile par ye stack ho jayega (upar-niche) with gr.Row(): # --- INPUT SECTION --- with gr.Column(scale=1): gr.Markdown("### 📤 UPLOAD") audio_input = gr.Audio( label="Source", type="filepath", sources=["upload", "microphone"], show_label=False ) gr.Markdown("### 🎚️ SETTING") silence_threshold = gr.Slider( minimum=0, maximum=1, step=0.01, value=0.05, label="Keep Silence (Sec)" ) submit_btn = gr.Button("🚀 PROCESS AUDIO", elem_classes="process-btn") # --- OUTPUT SECTION --- with gr.Column(scale=1): gr.Markdown("### 🎧 RESULT") audio_output = gr.Audio( label="Clean Audio", show_label=False, interactive=False ) with gr.Row(): file_output = gr.File(label="Download", file_count="single", scale=2) clear_btn = gr.Button("❌ RESET", elem_classes="reset-btn", scale=1) duration_output = gr.Textbox( label="STATS", lines=2, elem_classes="stat-box", show_label=True ) gr.Markdown("""
Made with ❤️
""") submit_btn.click( fn=process_audio, inputs=[audio_input, silence_threshold], outputs=[audio_output, file_output, duration_output] ) clear_btn.click( fn=reset_interface, inputs=None, outputs=[audio_input, silence_threshold, audio_output, file_output, duration_output] ) return demo @click.command() @click.option("--debug", is_flag=True, default=False) @click.option("--share", is_flag=True, default=False) def main(debug, share): demo = ui() demo.queue().launch(debug=debug, share=share) if __name__ == "__main__": main()