NeuralFalcon commited on
Commit
b085918
·
verified ·
1 Parent(s): 013b430

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +167 -412
app.py CHANGED
@@ -3,33 +3,91 @@ import os
3
  import uuid
4
  from pydub import AudioSegment
5
  from pydub.silence import split_on_silence
6
- import re
7
  import time
8
  import subprocess
9
  import threading
10
 
11
- # ─── File Helpers ─────────────────────────────────────────────────────────────
12
 
13
  def clean_file_name(file_path):
14
  file_name = os.path.basename(file_path)
15
  file_name, file_extension = os.path.splitext(file_name)
 
16
  cleaned = re.sub(r'[^a-zA-Z\d]+', '_', file_name)
17
- clean_name = re.sub(r'_+', '_', cleaned).strip('_')
18
- if clean_name.endswith('_tmp'):
19
- clean_name = clean_name[:-4]
 
 
 
20
  random_uuid = uuid.uuid4().hex[:6]
 
21
  clean_file_path = os.path.join(
22
- os.path.dirname(file_path),
23
- f"{clean_name}_{random_uuid}{file_extension}"
24
  )
 
25
  return clean_file_path
26
 
27
- def calculate_duration(file_path):
28
- audio = AudioSegment.from_file(file_path)
29
- return len(audio) / 1000.0
30
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
- # ─── File Tracking & Cleanup ──────────────────────────────────────────────────
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
  FILE_TIMESTAMPS = {}
35
 
@@ -39,17 +97,22 @@ def track_file(file_path):
39
  def cleanup_tracked_files(max_age_seconds=3600):
40
  now = time.time()
41
  to_delete = []
 
42
  for file_path, created_time in list(FILE_TIMESTAMPS.items()):
43
  if now - created_time > max_age_seconds:
44
  if os.path.exists(file_path):
45
  try:
46
  os.remove(file_path)
 
47
  except Exception as e:
48
- pass
 
49
  to_delete.append(file_path)
 
50
  for f in to_delete:
51
  FILE_TIMESTAMPS.pop(f, None)
52
 
 
53
  _CLEANUP_STARTED = False
54
 
55
  def start_cleanup_worker(interval=3600):
@@ -57,32 +120,22 @@ def start_cleanup_worker(interval=3600):
57
  if _CLEANUP_STARTED:
58
  return
59
  _CLEANUP_STARTED = True
 
60
  def worker():
61
  while True:
62
  cleanup_tracked_files()
63
  time.sleep(interval)
64
- threading.Thread(target=worker, daemon=True).start()
65
 
66
-
67
- # ─── Audio Conversion ─────────────────────────────────────────────────────────
68
 
69
  def convert_to_wav(audio_path):
70
  if not os.path.isfile(audio_path):
71
  return None
72
 
73
- # Get extension
74
- ext = os.path.splitext(audio_path)[1].lower()
75
-
76
- # If already mp3 or wav, return original file
77
- if ext in [".mp3", ".wav"]:
78
- return audio_path
79
-
80
- # Clean filename
81
  file_name = os.path.splitext(os.path.basename(audio_path))[0]
82
  clean_name = re.sub(r'[^a-zA-Z0-9]+', '_', file_name)
83
  clean_name = re.sub(r'_+', '_', clean_name).strip('_')
84
 
85
- # Output wav path
86
  wav_path = os.path.join(
87
  os.path.dirname(audio_path),
88
  f"{clean_name}_tmp.wav"
@@ -94,8 +147,6 @@ def convert_to_wav(audio_path):
94
  "ffmpeg",
95
  "-y",
96
  "-i", audio_path,
97
- "-ar", "16000",
98
- "-ac", "1",
99
  wav_path
100
  ],
101
  stdout=subprocess.DEVNULL,
@@ -106,427 +157,131 @@ def convert_to_wav(audio_path):
106
  if os.path.isfile(wav_path) and os.path.getsize(wav_path) > 0:
107
  return wav_path
108
 
109
- except Exception:
 
110
  pass
111
 
112
  return None
113
 
114
-
115
- # ───pydub ─────────────────────────────────────────────
116
-
117
-
118
- # def remove_silence_pydub(file_path, minimum_silence=50):
119
- # sound = AudioSegment.from_file(file_path) # auto-detects format
120
- # audio_chunks = split_on_silence(sound,
121
- # min_silence_len=100,
122
- # silence_thresh=-45,
123
- # keep_silence=minimum_silence)
124
- # combined = AudioSegment.empty()
125
- # for chunk in audio_chunks:
126
- # combined += chunk
127
- # output_path=clean_file_name(file_path)
128
- # combined.export(output_path)
129
- # return output_path
130
-
131
-
132
- def remove_silence_pydub(file_path, minimum_silence=50):
133
- sound = AudioSegment.from_file(file_path)
134
-
135
- # Try splitting with default -45 dBFS
136
- audio_chunks = split_on_silence(
137
- sound,
138
- min_silence_len=100,
139
- silence_thresh=-45,
140
- keep_silence=minimum_silence
141
- )
142
-
143
- # If no chunks were extracted (e.g. whole file is quieter than -45dBFS)
144
- # retry with a dynamic threshold relative to the audio's actual loudness
145
- if not audio_chunks:
146
- dynamic_thresh = sound.dBFS - 16
147
- audio_chunks = split_on_silence(
148
- sound,
149
- min_silence_len=100,
150
- silence_thresh=dynamic_thresh,
151
- keep_silence=minimum_silence
152
- )
153
-
154
- combined = AudioSegment.empty()
155
- for chunk in audio_chunks:
156
- combined += chunk
157
-
158
- if len(combined) == 0:
159
- combined = sound
160
-
161
- output_path = clean_file_name(file_path)
162
-
163
- ext = os.path.splitext(output_path)[1].lower().replace('.', '')
164
- if not ext:
165
- ext = "wav"
166
- output_path += ".wav"
167
-
168
- combined.export(output_path, format=ext)
169
- return output_path
170
-
171
-
172
-
173
- # ─── Main Processing ──────────────────────────────────────────────────────────
174
-
175
- def process_audio(audio_file, seconds_float):
176
  if audio_file is None:
177
- return None, None, ""
178
- if not os.path.exists(audio_file):
179
- return None, None, ""
180
 
181
- try:
182
- seconds = max(0.0, float(seconds_float))
183
- except ValueError:
184
- seconds = 0.05
185
 
186
  track_file(audio_file)
 
187
  converted_audio = convert_to_wav(audio_file)
188
-
189
  if converted_audio:
190
  track_file(converted_audio)
191
  audio_file = converted_audio
192
  else:
193
- return None, None, "Invalid file format or conversion failed."
194
-
195
  keep_silence = int(seconds * 1000)
196
 
197
  try:
198
  before = calculate_duration(audio_file)
199
-
200
- # Process strictly with PyDub
201
- output_audio_file = remove_silence_pydub(
202
  audio_file,
203
  minimum_silence=keep_silence
204
  )
205
-
206
  track_file(output_audio_file)
 
207
  after = calculate_duration(output_audio_file)
 
208
 
209
- removed = before - after
210
- percent = (removed / before * 100) if before > 0 else 0
211
-
212
- def fmt(s):
213
- m = int(s // 60)
214
- sec = s % 60
215
- if m > 0:
216
- return f"{m}m {sec:.1f}s"
217
- return f"{sec:.2f}s"
218
-
219
- mode_label = ""
220
-
221
- # Sleek, Dark-Themed Result Card
222
- result_html = f"""
223
- <div style="margin-top: 15px; background: #1c1c1c; border: 1px solid #333; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 10px rgba(0,0,0,0.5);">
224
- <div style="display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #333;">
225
-
226
- <div style="flex: 1; padding: 18px 10px; text-align: center; border-right: 1px solid #333;">
227
- <div style="font-size: 11px; color: #888; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 6px;">Original</div>
228
- <div style="font-size: 22px; font-weight: 700; color: #f4f4f5; font-family: 'Inter', sans-serif;">{fmt(before)}</div>
229
- </div>
230
-
231
- <div style="flex: 1; padding: 18px 10px; text-align: center; border-right: 1px solid #333; background: #222;">
232
- <div style="font-size: 11px; color: #888; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 6px;">New</div>
233
- <div style="font-size: 22px; font-weight: 700; color: #3b82f6; font-family: 'Inter', sans-serif;">{fmt(after)}</div>
234
- </div>
235
-
236
- <div style="flex: 1; padding: 18px 10px; text-align: center; background: #1c1c1c;">
237
- <div style="font-size: 11px; color: #888; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 6px;">Removed</div>
238
- <div style="font-size: 22px; font-weight: 700; color: #ef4444; font-family: 'Inter', sans-serif;">{percent:.1f}%</div>
239
- <div style="font-size: 12px; color: #666; margin-top: 4px; font-weight: 500;">{fmt(removed)}</div>
240
- </div>
241
 
242
- </div>
243
- """
244
- return output_audio_file, output_audio_file, result_html
 
245
 
246
- except Exception as e:
247
- return None, None, f"<p style='color:#ef4444; font-family:Inter,sans-serif; font-size:13px; margin-top:10px; padding:12px; background:#2a1111; border-radius:8px; border:1px solid #5a1a1a;'>Error: {str(e)}</p>"
248
-
249
-
250
- # -----------------------------
251
- # CSS FOR LAYOUT & THEMING
252
- # -----------------------------
253
-
254
- css = """
255
- /* APP & MAIN */
256
- body, html {
257
- background: #171717 !important;
258
- font-family: 'Inter', sans-serif !important;
259
- color: white !important;
260
- margin: 0;
261
- padding: 0;
262
- }
263
-
264
- /* 🟢 FORCING THE WIDE LAYOUT 🟢 */
265
- .gradio-container {
266
- max-width: 1500px !important; /* Forces container to stretch out */
267
- width: 95% !important; /* Uses 95% of the screen width */
268
- margin: auto !important;
269
- background: #171717 !important;
270
- padding: 20px 24px !important;
271
- }
272
-
273
- /* REMOVE DEFAULT FOOTER */
274
- footer { display: none !important; }
275
-
276
- .dark {
277
- --body-background-fill: #171717 !important;
278
- --block-background-fill: #262626 !important;
279
- --block-border-color: #333 !important;
280
- }
281
-
282
- /* TOPBAR */
283
- .topbar {
284
- display: flex;
285
- justify-content: space-between;
286
- align-items: center;
287
- margin-bottom: 25px;
288
- }
289
-
290
- .logo {
291
- display: flex;
292
- align-items: center;
293
- gap: 12px;
294
- }
295
-
296
- .logo-icon {
297
- width: 32px;
298
- height: 32px;
299
- border-radius: 8px;
300
- background: white;
301
- color: black;
302
- display: flex;
303
- align-items: center;
304
- justify-content: center;
305
- font-weight: 700;
306
- font-size: 15px;
307
- }
308
-
309
- .logo-text {
310
- font-size: 18px;
311
- font-weight: 600;
312
- }
313
-
314
- .nav {
315
- display: flex;
316
- gap: 22px;
317
- align-items: center;
318
- }
319
-
320
- .nav a {
321
- color: #a1a1aa;
322
- text-decoration: none;
323
- cursor: pointer;
324
- transition: 0.2s;
325
- font-size: 13px;
326
- font-weight: 500;
327
- }
328
-
329
- .nav a:hover {
330
- color: white;
331
- }
332
-
333
- .nav-yt { color: #ff4e4e !important; }
334
- .nav-kofi { color: #29abe0 !important; }
335
-
336
- /* HERO */
337
- .hero {
338
- text-align: center;
339
- margin-bottom: 35px;
340
- }
341
-
342
- .hero h1 {
343
- font-size: 42px;
344
- font-weight: 800;
345
- letter-spacing: -0.05em;
346
- margin-bottom: 6px;
347
- }
348
-
349
- /* BLUE HERO TEXT */
350
- .hero span {
351
- color: #3b82f6;
352
- }
353
-
354
- .hero p {
355
- color: #a1a1aa;
356
- font-size: 15px;
357
- margin: 4px 0 16px 0;
358
- }
359
-
360
- .badges {
361
- display: flex;
362
- justify-content: center;
363
- gap: 10px;
364
- }
365
-
366
- .badge {
367
- background: #262626;
368
- border: 1px solid #333;
369
- padding: 6px 14px;
370
- border-radius: 999px;
371
- color: #d4d4d8;
372
- font-size: 11px;
373
- font-weight: 500;
374
- }
375
-
376
- /* CARDS / PANELS */
377
- .panel {
378
- background: #262626 !important;
379
- border: 1px solid #333 !important;
380
- border-radius: 14px !important;
381
- padding: 24px !important;
382
- box-shadow: 0 4px 12px rgba(0,0,0,0.15) !important;
383
- }
384
-
385
- /* FORCE SIDE BY SIDE ON DESKTOP */
386
- @media (min-width: 768px) {
387
- .side-by-side {
388
- flex-wrap: nowrap !important;
389
  }
390
- }
391
-
392
- /* TITLES */
393
- .section-title {
394
- font-size: 12px;
395
- text-transform: uppercase;
396
- color: #888;
397
- letter-spacing: 1px;
398
- margin-bottom: 14px;
399
- font-weight: 600;
400
- }
401
-
402
- /* AUDIO COMPONENT */
403
- audio {
404
- border-radius: 8px !important;
405
- background: #1f1f1f !important;
406
- }
407
-
408
- /* CUSTOM BLUE BUTTON */
409
- #process-btn {
410
- background: #3b82f6 !important;
411
- color: white !important;
412
- border: none !important;
413
- border-radius: 8px !important;
414
- height: 48px !important;
415
- font-size: 15px !important;
416
- font-weight: 600 !important;
417
- margin-top: 14px !important;
418
- transition: all 0.2s ease-in-out !important;
419
- box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3) !important;
420
- }
421
-
422
- #process-btn:hover {
423
- background: #2563eb !important;
424
- box-shadow: 0 4px 16px rgba(59, 130, 246, 0.4) !important;
425
- transform: translateY(-1px);
426
- }
427
-
428
- /* INPUTS */
429
- .gr-textbox, .gr-dropdown {
430
- background: #1f1f1f !important;
431
- border: 1px solid #3a3a3a !important;
432
- border-radius: 8px !important;
433
- }
434
-
435
- /* MOBILE RESPONSIVENESS */
436
- @media(max-width: 900px) {
437
- .topbar { flex-direction: column; gap: 16px; }
438
- .nav { flex-wrap: wrap; justify-content: center; }
439
- }
440
- """
441
-
442
- # -----------------------------
443
- # START BACKGROUND WORKERS
444
- # -----------------------------
445
- start_cleanup_worker()
446
 
447
- # -----------------------------
448
- # UI
449
- # -----------------------------
450
-
451
- with gr.Blocks(
452
- theme=gr.themes.Base(),
453
- css=css,
454
- title="Remove Silence"
455
- ) as demo:
456
-
457
- # TOPBAR
458
- gr.HTML("""
459
- <div class="topbar">
460
- <div class="logo">
461
- <div class="logo-icon">R</div>
462
- <div class="logo-text">Remove Silence</div>
463
- </div>
464
- <div class="nav">
465
- <a href="https://www.youtube.com/@neuralfalcon/" target="_blank" class="nav-yt">Subscribe YouTube</a>
466
- <a href="https://ko-fi.com/neuralfalcon" target="_blank" class="nav-kofi">Donate</a>
467
- <a href="https://github.com/NeuralFalconYT/Remove-Silence-From-Audio" target="_blank">GitHub</a>
468
- <a href="mailto:NeuralFalcon@proton.me" target="_blank">Mail</a>
469
- <a href="https://x.com/NeuralFalcon" target="_blank">X</a>
470
- </div>
471
- </div>
472
- """)
473
-
474
- # HERO
475
- gr.HTML("""
476
- <div class="hero">
477
- <h1>REMOVE <span>SILENCE</span></h1>
478
- <p>Drop your audio and instantly clean pauses for Shorts, TikTok & Reels</p>
479
- <div class="badges">
480
- <div class="badge">100% Free</div>
481
- <div class="badge">No Sign-Up</div>
482
  </div>
483
- </div>
484
- """)
485
-
486
- with gr.Row(equal_height=False, elem_classes="side-by-side"):
487
-
488
- # LEFT PANEL (UPLOAD) - Adjusted min_width to stretch beautifully in the new wide container
489
- with gr.Column(scale=1, min_width=450):
490
- with gr.Group(elem_classes="panel"):
491
- gr.HTML('<div class="section-title">Upload & Settings</div>')
492
-
493
- input_audio = gr.Audio(
494
  type="filepath",
495
- label="",
496
- show_label=False
497
  )
498
 
499
- with gr.Row():
500
- keep_silence = gr.Number(
501
  label="Keep Silence Upto (In seconds)",
502
  value=0.05
503
  )
504
 
505
- # Blue submit button
506
- process_btn = gr.Button("Remove Silence", elem_id="process-btn")
 
 
507
 
508
- # RIGHT PANEL (RESULT)
509
- with gr.Column(scale=1, min_width=450):
510
- with gr.Group(elem_classes="panel"):
511
- gr.HTML('<div class="section-title">Result</div>')
512
 
513
- output_audio = gr.Audio(
514
- label="Play Audio",
515
- show_label=False
516
- )
517
-
518
- download_audio = gr.File(
519
- label="Download Audio",
520
- show_label=False
521
- )
522
-
523
- stats_html = gr.HTML()
524
-
525
- # PROCESS
526
- process_btn.click(
527
- fn=process_audio,
528
- inputs=[input_audio, keep_silence],
529
- outputs=[output_audio, download_audio, stats_html]
530
- )
531
 
532
- demo.launch()
 
 
 
3
  import uuid
4
  from pydub import AudioSegment
5
  from pydub.silence import split_on_silence
6
+ import re
7
  import time
8
  import subprocess
9
  import threading
10
 
11
+ # gr.close_all()
12
 
13
  def clean_file_name(file_path):
14
  file_name = os.path.basename(file_path)
15
  file_name, file_extension = os.path.splitext(file_name)
16
+
17
  cleaned = re.sub(r'[^a-zA-Z\d]+', '_', file_name)
18
+
19
+ clean_file_name = re.sub(r'_+', '_', cleaned).strip('_')
20
+
21
+ if clean_file_name.endswith('_tmp'):
22
+ clean_file_name = clean_file_name[:-4]
23
+
24
  random_uuid = uuid.uuid4().hex[:6]
25
+
26
  clean_file_path = os.path.join(
27
+ os.path.dirname(file_path),
28
+ f"{clean_file_name}_{random_uuid}{file_extension}"
29
  )
30
+
31
  return clean_file_path
32
 
 
 
 
33
 
34
+ # def remove_silence(file_path, minimum_silence=50):
35
+ # sound = AudioSegment.from_file(file_path) # auto-detects format
36
+ # audio_chunks = split_on_silence(sound,
37
+ # min_silence_len=100,
38
+ # silence_thresh=-45,
39
+ # keep_silence=minimum_silence)
40
+ # combined = AudioSegment.empty()
41
+ # for chunk in audio_chunks:
42
+ # combined += chunk
43
+ # output_path=clean_file_name(file_path)
44
+ # combined.export(output_path)
45
+ # return output_path
46
 
47
+
48
+ def remove_silence(file_path, minimum_silence=50):
49
+ sound = AudioSegment.from_file(file_path)
50
+
51
+ # Try splitting with default -45 dBFS
52
+ audio_chunks = split_on_silence(
53
+ sound,
54
+ min_silence_len=100,
55
+ silence_thresh=-45,
56
+ keep_silence=minimum_silence
57
+ )
58
+
59
+ # If no chunks were extracted (e.g. whole file is quieter than -45dBFS)
60
+ # retry with a dynamic threshold relative to the audio's actual loudness
61
+ if not audio_chunks:
62
+ dynamic_thresh = sound.dBFS - 16
63
+ audio_chunks = split_on_silence(
64
+ sound,
65
+ min_silence_len=100,
66
+ silence_thresh=dynamic_thresh,
67
+ keep_silence=minimum_silence
68
+ )
69
+
70
+ combined = AudioSegment.empty()
71
+ for chunk in audio_chunks:
72
+ combined += chunk
73
+
74
+ if len(combined) == 0:
75
+ combined = sound
76
+
77
+ output_path = clean_file_name(file_path)
78
+
79
+ ext = os.path.splitext(output_path)[1].lower().replace('.', '')
80
+ if not ext:
81
+ ext = "wav"
82
+ output_path += ".wav"
83
+
84
+ combined.export(output_path, format=ext)
85
+ return output_path
86
+
87
+ def calculate_duration(file_path):
88
+ audio = AudioSegment.from_file(file_path)
89
+ duration_seconds = len(audio) / 1000.0
90
+ return duration_seconds
91
 
92
  FILE_TIMESTAMPS = {}
93
 
 
97
  def cleanup_tracked_files(max_age_seconds=3600):
98
  now = time.time()
99
  to_delete = []
100
+
101
  for file_path, created_time in list(FILE_TIMESTAMPS.items()):
102
  if now - created_time > max_age_seconds:
103
  if os.path.exists(file_path):
104
  try:
105
  os.remove(file_path)
106
+ print(f"🗑️ Deleted: {file_path}")
107
  except Exception as e:
108
+ print(f"⚠️ Error deleting {file_path}: {e}")
109
+
110
  to_delete.append(file_path)
111
+
112
  for f in to_delete:
113
  FILE_TIMESTAMPS.pop(f, None)
114
 
115
+
116
  _CLEANUP_STARTED = False
117
 
118
  def start_cleanup_worker(interval=3600):
 
120
  if _CLEANUP_STARTED:
121
  return
122
  _CLEANUP_STARTED = True
123
+
124
  def worker():
125
  while True:
126
  cleanup_tracked_files()
127
  time.sleep(interval)
 
128
 
129
+ threading.Thread(target=worker, daemon=True).start()
 
130
 
131
  def convert_to_wav(audio_path):
132
  if not os.path.isfile(audio_path):
133
  return None
134
 
 
 
 
 
 
 
 
 
135
  file_name = os.path.splitext(os.path.basename(audio_path))[0]
136
  clean_name = re.sub(r'[^a-zA-Z0-9]+', '_', file_name)
137
  clean_name = re.sub(r'_+', '_', clean_name).strip('_')
138
 
 
139
  wav_path = os.path.join(
140
  os.path.dirname(audio_path),
141
  f"{clean_name}_tmp.wav"
 
147
  "ffmpeg",
148
  "-y",
149
  "-i", audio_path,
 
 
150
  wav_path
151
  ],
152
  stdout=subprocess.DEVNULL,
 
157
  if os.path.isfile(wav_path) and os.path.getsize(wav_path) > 0:
158
  return wav_path
159
 
160
+ except Exception as e:
161
+ print(f"⚠️ FFmpeg conversion error: {e}")
162
  pass
163
 
164
  return None
165
 
166
+ def process_audio(audio_file, seconds=0.05):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  if audio_file is None:
168
+ return None, None, "No file uploaded"
 
 
169
 
170
+ if not os.path.exists(audio_file):
171
+ return None, None, "File not found"
 
 
172
 
173
  track_file(audio_file)
174
+
175
  converted_audio = convert_to_wav(audio_file)
176
+
177
  if converted_audio:
178
  track_file(converted_audio)
179
  audio_file = converted_audio
180
  else:
181
+ return None, None, "Invalid file format or conversion failed"
182
+
183
  keep_silence = int(seconds * 1000)
184
 
185
  try:
186
  before = calculate_duration(audio_file)
187
+
188
+ output_audio_file = remove_silence(
 
189
  audio_file,
190
  minimum_silence=keep_silence
191
  )
192
+
193
  track_file(output_audio_file)
194
+
195
  after = calculate_duration(output_audio_file)
196
+ text = f"Old Duration: {before:.3f} Seconds \nNew Duration: {after:.3f} Seconds"
197
 
198
+ return output_audio_file, output_audio_file, text
199
+
200
+ except Exception as e:
201
+ print(f"⚠️ Error processing audio: {e}")
202
+ return None, None, f"An error occurred during processing: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
 
204
+ def ui():
205
+ theme = gr.themes.Soft(
206
+ font=[gr.themes.GoogleFont("Source Sans Pro"), "Arial", "sans-serif"]
207
+ )
208
 
209
+ css = """
210
+ .gradio-container {max-width: none !important;}
211
+ .tab-content {padding: 20px;}
212
+
213
+ /* Primary button - BLUE by default */
214
+ button.primary {
215
+ background-color: #2563eb !important;
216
+ color: white !important;
217
+ font-weight: 600;
218
+ border: none !important;
219
+ border-radius: 10px;
220
+ padding: 12px 18px;
221
+ font-size: 1.05em;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
 
224
+ button.primary:hover {
225
+ background-color: #1e40af !important;
226
+ }
227
+ """
228
+
229
+ with gr.Blocks(theme=theme, css=css) as demo:
230
+
231
+ gr.HTML("""
232
+ <div style="text-align:center; margin:20px auto; max-width:800px;">
233
+ <h1 style="font-size:2.4em; margin-bottom:6px;">
234
+ 🔇 Remove Silence From Audio
235
+ </h1>
236
+
237
+ <p style="font-size:1.05em; color:#555; margin:0 0 10px;">
238
+ Upload an Audio file, and it will remove silent parts from it.
239
+ </p>
240
+ <p style="font-size:0.8em; color:#999;">
241
+ ⚠️ Please don’t upload copyrighted content — it can take this Space offline.
242
+ </p>
243
+ <p style="font-size:0.9em; color:#777;">
244
+ Install locally on your computer, enjoy unlimited runs with no waiting queue
245
+ <a href="https://github.com/NeuralFalconYT/Remove-Silence-From-Audio" target="_blank" style="text-decoration:none;">
246
+ Download Link
247
+ </a>
248
+ </p>
 
 
 
 
 
 
 
 
 
 
249
  </div>
250
+ """)
251
+
252
+ with gr.Row():
253
+ with gr.Column(scale=1):
254
+ audio_input = gr.Audio(
255
+ label="Upload Audio",
 
 
 
 
 
256
  type="filepath",
257
+ sources=["upload", "microphone"]
 
258
  )
259
 
260
+ silence_threshold = gr.Number(
 
261
  label="Keep Silence Upto (In seconds)",
262
  value=0.05
263
  )
264
 
265
+ submit_btn = gr.Button(
266
+ "🔇 Remove Silence",
267
+ variant="primary"
268
+ )
269
 
270
+ with gr.Column(scale=1):
271
+ audio_output = gr.Audio(label="Play Audio")
272
+ file_output = gr.File(label="Download Audio File")
273
+ duration_output = gr.Textbox(label="Duration", lines=2)
274
 
275
+ submit_btn.click(
276
+ fn=process_audio,
277
+ inputs=[audio_input, silence_threshold],
278
+ outputs=[audio_output, file_output, duration_output]
279
+ )
280
+
281
+ return demo
282
+
283
+ start_cleanup_worker()
 
 
 
 
 
 
 
 
 
284
 
285
+ demo= ui()
286
+ demo.queue().launch()
287
+ # demo.queue().launch(debug=True, share=True)