Offex commited on
Commit
e44537b
·
verified ·
1 Parent(s): b54912c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +179 -136
app.py CHANGED
@@ -1,172 +1,215 @@
1
  import gradio as gr
2
  import os
3
- import tempfile
4
  from pydub import AudioSegment
5
  from pydub.silence import split_on_silence
 
6
  import click
7
 
8
- # --- Logic Section ---
9
-
10
- def remove_silence_logic(file_path, silence_thresh, min_silence_len, keep_silence_sec, progress=gr.Progress()):
11
- try:
12
- progress(0, desc="Loading Audio...")
13
- # Load the audio file
14
- try:
15
- sound = AudioSegment.from_file(file_path)
16
- except Exception as e:
17
- raise gr.Error(f"Could not load audio: {str(e)}")
18
-
19
- original_duration = len(sound) / 1000.0
20
-
21
- # Convert keep_silence from seconds to ms
22
- keep_silence_ms = int(keep_silence_sec * 1000)
23
-
24
- progress(0.2, desc="Analyzing Silence...")
25
-
26
- # Split the audio
27
- # silence_thresh: usually negative (e.g. -40).
28
- # min_silence_len: in ms.
29
- audio_chunks = split_on_silence(
30
- sound,
31
- min_silence_len=int(min_silence_len),
32
- silence_thresh=silence_thresh,
33
- keep_silence=keep_silence_ms
34
- )
35
-
36
- progress(0.6, desc="Recombining Audio...")
37
-
38
- if not audio_chunks:
39
- # If aggressive settings removed everything, return original or warning
40
- raise gr.Error("Settings too aggressive! No audio remained. Try lowering the dB threshold.")
41
-
42
- combined = AudioSegment.empty()
43
- for chunk in audio_chunks:
44
- combined += chunk
45
-
46
- new_duration = len(combined) / 1000.0
47
-
48
- # Export logic using tempfile to avoid permission issues
49
- progress(0.8, desc="Exporting File...")
50
-
51
- # Create a temp file with the same extension
52
- file_ext = os.path.splitext(file_path)[1]
53
- if not file_ext:
54
- file_ext = ".wav" # Default fallback
55
-
56
- with tempfile.NamedTemporaryFile(delete=False, suffix=file_ext) as temp_file:
57
- output_path = temp_file.name
58
-
59
- # Export
60
- combined.export(output_path, format=file_ext.replace(".", ""))
61
-
62
- stats = (
63
- f"🟢 **Original Duration:** {original_duration:.2f}s\n"
64
- f"🔴 **New Duration:** {new_duration:.2f}s\n"
65
- f"✂️ **Time Saved:** {original_duration - new_duration:.2f}s"
66
- )
67
-
68
- return output_path, output_path, stats
69
-
70
- except Exception as e:
71
- raise gr.Error(f"Error processing audio: {str(e)}")
72
-
73
-
74
- # --- UI Section ---
75
 
76
  def ui():
77
- # Modern Soft Theme
78
  theme = gr.themes.Soft(
79
- primary_hue="blue",
80
  secondary_hue="slate",
81
- font=[gr.themes.GoogleFont("Inter"), "Arial", "sans-serif"]
 
82
  )
83
 
 
84
  css = """
85
- .gradio-container {max-width: 900px !important; margin: auto;}
86
- h1 {text-align: center; color: #2563eb;}
87
- .stat-box {background: #f1f5f9; padding: 15px; border-radius: 10px; border: 1px solid #e2e8f0;}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  """
89
 
90
- with gr.Blocks(theme=theme, css=css, title="Silence Remover") as demo:
91
 
92
- # Header
93
- gr.Markdown(
94
- """
95
- # 🔇 Smart Silence Remover
96
- Upload an audio file to automatically remove silent pauses.
97
- Adjust the **Advanced Settings** if the result is not perfect.
98
- """
99
- )
100
-
101
- with gr.Row():
102
- # Left Column: Inputs
103
- with gr.Column(scale=1):
104
- audio_input = gr.Audio(
105
- label="Input Audio",
106
- type="filepath",
107
- sources=["upload", "microphone"]
108
- )
109
-
110
- # Basic Controls
111
- keep_silence_input = gr.Slider(
112
- minimum=0, maximum=2, step=0.01, value=0.1,
113
- label="Padding (Keep Silence)",
114
- info="How much silence (in seconds) to keep around words so they don't sound chopped."
115
- )
116
-
117
- # Advanced Controls inside Accordion
118
- with gr.Accordion("⚙️ Advanced Settings", open=False):
119
- thresh_input = gr.Slider(
120
- minimum=-80, maximum=0, step=1, value=-40,
121
- label="Silence Threshold (dB)",
122
- info="Anything quieter than this is considered silence. Lower = stricter."
123
  )
124
- min_len_input = gr.Number(
125
- value=500,
126
- label="Minimum Silence Length (ms)",
127
- info="Minimum length of a pause to be removed."
 
 
 
 
 
 
 
128
  )
129
 
130
- submit_btn = gr.Button("✨ Process Audio", variant="primary", size="lg")
131
-
132
- # Right Column: Outputs
133
- with gr.Column(scale=1):
134
- # Output Audio player
135
- audio_output = gr.Audio(label="Result", interactive=False)
136
- # Download button
137
- file_output = gr.DownloadButton(label="Download Processed Audio")
138
- # Stats
139
- stats_output = gr.Markdown(value="Waiting for audio...", elem_classes=["stat-box"])
 
 
 
 
 
 
 
 
 
 
140
 
141
- # Event Handling
142
- submit_btn.click(
143
- fn=remove_silence_logic,
144
- inputs=[audio_input, thresh_input, min_len_input, keep_silence_input],
145
- outputs=[audio_output, file_output, stats_output]
146
- )
147
-
148
  # Footer
149
  gr.Markdown(
150
  """
151
- <div style="text-align: center; margin-top: 20px; font-size: 0.8em; color: gray;">
152
- Powered by Pydub & Gradio | Made with ❤️ by NeuralFalconYT
153
  </div>
154
  """
155
  )
156
 
 
 
 
 
 
 
 
157
  return demo
158
 
159
- # --- Main Entry Point ---
160
 
161
  @click.command()
162
  @click.option("--debug", is_flag=True, default=False, help="Enable debug mode.")
163
- @click.option("--share", is_flag=True, default=False, help="Create a public link.")
164
- @click.option("--port", default=7860, help="Port to run the server on.")
165
- def main(debug, share, port):
166
  demo = ui()
167
- # queue() is essential for processing audio without freezing the UI
168
- demo.queue().launch(debug=debug, share=share, server_port=port)
169
 
170
  if __name__ == "__main__":
171
  main()
172
-
 
1
  import gradio as gr
2
  import os
3
+ import uuid
4
  from pydub import AudioSegment
5
  from pydub.silence import split_on_silence
6
+ import re
7
  import click
8
 
9
+ # --- LOGIC SECTION (Bilkul Same hai, No Changes) ---
10
+
11
+ def clean_file_name(file_path):
12
+ # Get the base file name and extension
13
+ file_name = os.path.basename(file_path)
14
+ file_name, file_extension = os.path.splitext(file_name)
15
+
16
+ # Replace non-alphanumeric characters with an underscore
17
+ cleaned = re.sub(r'[^a-zA-Z\d]+', '_', file_name)
18
+
19
+ # Remove any multiple underscores
20
+ clean_file_name = re.sub(r'_+', '_', cleaned).strip('_')
21
+
22
+ # Generate a random UUID for uniqueness
23
+ random_uuid = uuid.uuid4().hex[:6]
24
+
25
+ # Combine cleaned file name with the original extension
26
+ clean_file_path = os.path.join(os.path.dirname(file_path), clean_file_name + f"_{random_uuid}" + file_extension)
27
+
28
+ return clean_file_path
29
+
30
+ def remove_silence(file_path, minimum_silence=50):
31
+ sound = AudioSegment.from_file(file_path) # auto-detects format
32
+ # Logic wahi purana hai (-45dB)
33
+ audio_chunks = split_on_silence(sound,
34
+ min_silence_len=100,
35
+ silence_thresh=-45,
36
+ keep_silence=minimum_silence)
37
+ combined = AudioSegment.empty()
38
+ for chunk in audio_chunks:
39
+ combined += chunk
40
+ output_path = clean_file_name(file_path)
41
+ combined.export(output_path) # format inferred from output file extension
42
+ return output_path
43
+
44
+ def calculate_duration(file_path):
45
+ audio = AudioSegment.from_file(file_path)
46
+ duration_seconds = len(audio) / 1000.0 # pydub uses milliseconds
47
+ return duration_seconds
48
+
49
+ def process_audio(audio_file, seconds=0.05):
50
+ keep_silence = int(seconds * 1000)
51
+ output_audio_file = remove_silence(audio_file, minimum_silence=keep_silence)
52
+ before = calculate_duration(audio_file)
53
+ after = calculate_duration(output_audio_file)
54
+ # Output formatting thoda clean kiya hai text mein
55
+ text = f"⏱️ Old Duration: {before:.3f}s \n✅ New Duration: {after:.3f}s \n🚀 Saved: {before-after:.3f}s"
56
+ return output_audio_file, output_audio_file, text
57
+
58
+ # --- NEW UI SECTION (Visual Upgrade Only) ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
  def ui():
61
+ # Theme settings for a softer, modern look
62
  theme = gr.themes.Soft(
63
+ primary_hue="indigo",
64
  secondary_hue="slate",
65
+ font=[gr.themes.GoogleFont("Inter"), "Arial", "sans-serif"],
66
+ radius_size="lg"
67
  )
68
 
69
+ # Custom CSS for shadow, gradients, and layout
70
  css = """
71
+ .gradio-container {
72
+ max-width: 950px !important;
73
+ margin: auto;
74
+ padding-top: 20px;
75
+ }
76
+
77
+ /* Title Gradient */
78
+ .title-text {
79
+ background: linear-gradient(to right, #4f46e5, #9333ea);
80
+ -webkit-background-clip: text;
81
+ -webkit-text-fill-color: transparent;
82
+ font-weight: 800;
83
+ font-size: 2.5rem;
84
+ text-align: center;
85
+ margin-bottom: 0.5rem;
86
+ }
87
+
88
+ .subtitle-text {
89
+ text-align: center;
90
+ color: #64748b;
91
+ font-size: 1.1rem;
92
+ margin-bottom: 2rem;
93
+ }
94
+
95
+ /* Main Card Styling */
96
+ .main-card {
97
+ background: white;
98
+ border: 1px solid #e2e8f0;
99
+ border-radius: 16px;
100
+ box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
101
+ padding: 30px;
102
+ }
103
+
104
+ /* Button Styling */
105
+ .action-btn {
106
+ background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%) !important;
107
+ border: none !important;
108
+ color: white !important;
109
+ font-weight: 600 !important;
110
+ font-size: 1.1rem !important;
111
+ transition: transform 0.2s ease, box-shadow 0.2s ease !important;
112
+ margin-top: 15px !important;
113
+ }
114
+ .action-btn:hover {
115
+ transform: translateY(-2px);
116
+ box-shadow: 0 10px 20px rgba(79, 70, 229, 0.3) !important;
117
+ }
118
+
119
+ /* Output Box Highlight */
120
+ .result-box textarea {
121
+ font-family: 'Courier New', monospace;
122
+ font-weight: bold;
123
+ color: #0f172a !important;
124
+ background-color: #f1f5f9 !important;
125
+ border: 1px solid #cbd5e1 !important;
126
+ }
127
+
128
+ .dark .main-card {
129
+ background: #1e293b;
130
+ border-color: #334155;
131
+ }
132
  """
133
 
134
+ with gr.Blocks(theme=theme, css=css, title="Silence Remover Pro") as demo:
135
 
136
+ # Header Area
137
+ gr.HTML("""
138
+ <div class="title-text">Silence Remover Pro</div>
139
+ <div class="subtitle-text">Clean your audio files instantly by removing dead air and silence.</div>
140
+ """)
141
+
142
+ with gr.Column(elem_classes="main-card"):
143
+ with gr.Row(variant="panel", equal_height=True):
144
+ # LEFT COLUMN: INPUTS
145
+ with gr.Column(scale=1):
146
+ gr.Markdown("### 📥 Input Source")
147
+ audio_input = gr.Audio(
148
+ label="Upload Audio File",
149
+ type="filepath",
150
+ sources=["upload", "microphone"],
151
+ show_label=False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  )
153
+
154
+ gr.Markdown("### ⚙️ Settings")
155
+ silence_threshold = gr.Number(
156
+ label="Keep Silence Upto (Seconds)",
157
+ value=0.05,
158
+ info="Chote pauses ko rakhne ke liye time set karein."
159
+ )
160
+
161
+ submit_btn = gr.Button(
162
+ "✨ Remove Silence Now",
163
+ elem_classes="action-btn"
164
  )
165
 
166
+ # RIGHT COLUMN: OUTPUTS
167
+ with gr.Column(scale=1):
168
+ gr.Markdown("### 📤 Processed Result")
169
+ audio_output = gr.Audio(
170
+ label="Final Audio",
171
+ show_label=False,
172
+ interactive=False
173
+ )
174
+
175
+ with gr.Group():
176
+ file_output = gr.File(
177
+ label="Download Cleaned File",
178
+ file_count="single"
179
+ )
180
+ duration_output = gr.Textbox(
181
+ label="Statistics",
182
+ lines=3,
183
+ elem_classes="result-box",
184
+ show_label=True
185
+ )
186
 
 
 
 
 
 
 
 
187
  # Footer
188
  gr.Markdown(
189
  """
190
+ <div style="text-align: center; margin-top: 20px; font-size: 0.9em; color: #94a3b8;">
191
+ AI Powered Tool Fast & Secure
192
  </div>
193
  """
194
  )
195
 
196
+ # Actions
197
+ submit_btn.click(
198
+ fn=process_audio,
199
+ inputs=[audio_input, silence_threshold],
200
+ outputs=[audio_output, file_output, duration_output]
201
+ )
202
+
203
  return demo
204
 
205
+ # --- MAIN EXECUTION ---
206
 
207
  @click.command()
208
  @click.option("--debug", is_flag=True, default=False, help="Enable debug mode.")
209
+ @click.option("--share", is_flag=True, default=False, help="Enable sharing of the interface.")
210
+ def main(debug, share):
 
211
  demo = ui()
212
+ demo.queue().launch(debug=debug, share=share)
 
213
 
214
  if __name__ == "__main__":
215
  main()