Spaces:
Running
Running
| 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) |