Spaces:
Sleeping
Sleeping
import gradio as gr | |
from moviepy.editor import VideoFileClip, concatenate_videoclips | |
import numpy as np | |
from scipy.io import wavfile | |
import tempfile | |
import os | |
from pathlib import Path | |
def detect_silence(audio_array, sample_rate, threshold=0.01, min_silence_len=1000): | |
"""Detecta períodos de silêncio no áudio""" | |
# Converte o threshold para amplitude | |
amplitude_threshold = threshold * np.max(np.abs(audio_array)) | |
# Calcula a energia do áudio | |
energy = np.abs(audio_array) | |
if len(energy.shape) > 1: | |
energy = np.mean(energy, axis=1) | |
# Encontra regiões não silenciosas | |
is_sound = energy > amplitude_threshold | |
# Converte frames para segundos | |
frame_length = int(sample_rate * (min_silence_len / 1000)) | |
# Suaviza a detecção para evitar cortes muito curtos | |
sound_chunks = [] | |
start = None | |
for i in range(len(is_sound)): | |
if start is None and is_sound[i]: | |
start = i | |
elif start is not None and not is_sound[i]: | |
if i - start > frame_length: | |
sound_chunks.append((start / sample_rate, i / sample_rate)) | |
start = None | |
if start is not None: | |
sound_chunks.append((start / sample_rate, len(is_sound) / sample_rate)) | |
return sound_chunks | |
def process_video(video_path, threshold=0.01, min_silence_len=1000): | |
"""Remove silêncio do vídeo""" | |
# Carrega o vídeo | |
video = VideoFileClip(video_path) | |
# Extrai o áudio para análise | |
audio_array = video.audio.to_soundarray() | |
sample_rate = video.audio.fps | |
# Detecta regiões não silenciosas | |
sound_chunks = detect_silence(audio_array, sample_rate, threshold, min_silence_len) | |
if not sound_chunks: | |
video.close() | |
return video_path | |
# Corta e concatena os segmentos não silenciosos | |
clips = [] | |
for start, end in sound_chunks: | |
clip = video.subclip(start, end) | |
clips.append(clip) | |
# Concatena os clips | |
final_clip = concatenate_videoclips(clips) | |
# Salva o resultado | |
output_path = str(Path(video_path).parent / f"processed_{Path(video_path).name}") | |
final_clip.write_videofile(output_path) | |
# Limpa os recursos | |
video.close() | |
final_clip.close() | |
for clip in clips: | |
clip.close() | |
return output_path | |
def remove_silence(video_input, silence_duration, silence_threshold): | |
"""Interface para remoção normal de silêncio""" | |
try: | |
if video_input is None: | |
raise ValueError("Por favor, faça upload de um vídeo") | |
# Converte o threshold de dB para amplitude relativa | |
amplitude_threshold = 10 ** (silence_threshold / 20) | |
return process_video( | |
video_input, | |
threshold=amplitude_threshold, | |
min_silence_len=int(silence_duration * 1000) | |
) | |
except Exception as e: | |
gr.Error(str(e)) | |
return None | |
def remove_max_silence(video_input): | |
"""Interface para remoção máxima de silêncio""" | |
try: | |
if video_input is None: | |
raise ValueError("Por favor, faça upload de um vídeo") | |
# Configurações agressivas para máxima remoção | |
return process_video( | |
video_input, | |
threshold=0.05, # Mais sensível ao som | |
min_silence_len=100 # Remove silêncios mais curtos | |
) | |
except Exception as e: | |
gr.Error(str(e)) | |
return None | |
# Interface Gradio | |
with gr.Blocks(title="Removedor de Silêncio de Vídeos") as app: | |
gr.Markdown("# Removedor de Silêncio de Vídeos") | |
with gr.Row(): | |
with gr.Column(): | |
video_input = gr.Video( | |
label="Selecione ou Arraste o Vídeo" | |
) | |
with gr.Row(): | |
remove_max_btn = gr.Button("🔇 Remover 100% do Silêncio", variant="primary") | |
remove_custom_btn = gr.Button("Remover Silêncio Personalizado") | |
with gr.Group(): | |
gr.Markdown("### Configurações Personalizadas") | |
silence_duration = gr.Slider( | |
minimum=0.1, | |
maximum=5.0, | |
value=1.0, | |
step=0.1, | |
label="Duração Mínima do Silêncio (segundos)" | |
) | |
silence_threshold = gr.Slider( | |
minimum=-60, | |
maximum=-20, | |
value=-40, | |
step=1, | |
label="Limite de Silêncio (dB)" | |
) | |
with gr.Row(): | |
video_output = gr.Video(label="Vídeo Processado") | |
# Event handlers | |
remove_max_btn.click( | |
fn=remove_max_silence, | |
inputs=[video_input], | |
outputs=[video_output] | |
) | |
remove_custom_btn.click( | |
fn=remove_silence, | |
inputs=[ | |
video_input, | |
silence_duration, | |
silence_threshold | |
], | |
outputs=[video_output] | |
) | |
if __name__ == "__main__": | |
app.launch(show_error=True) |