DHEIVER's picture
Update app.py
6b4eada verified
raw
history blame
6.38 kB
import gradio as gr
import subprocess
import tempfile
import os
from pathlib import Path
import json
import shutil
def detect_silence_ffmpeg(video_path, silence_thresh=-40, min_silence_len=1):
"""Detecta silêncio usando FFmpeg diretamente, muito mais rápido que pydub"""
command = [
'ffmpeg', '-i', video_path,
'-af', f'silencedetect=noise={silence_thresh}dB:d={min_silence_len}',
'-f', 'null', '-'
]
# Executa FFmpeg e captura a saída de erro (onde está a informação do silêncio)
result = subprocess.run(command, stderr=subprocess.PIPE, text=True)
# Processa a saída para encontrar timestamps
silence_data = []
start_times = []
end_times = []
for line in result.stderr.split('\n'):
if 'silence_start' in line:
start_time = float(line.split('silence_start: ')[1].split()[0])
start_times.append(start_time)
elif 'silence_end' in line:
end_time = float(line.split('silence_end: ')[1].split()[0])
end_times.append(end_time)
# Cria lista de intervalos não silenciosos
if not start_times:
return []
nonsilent_ranges = []
video_duration = float(get_video_duration(video_path))
# Adiciona segmento do início até o primeiro silêncio
if start_times[0] > 0:
nonsilent_ranges.append((0, start_times[0]))
# Adiciona segmentos entre silêncios
for i in range(len(end_times)):
if i < len(start_times):
nonsilent_ranges.append((end_times[i], start_times[i]))
# Adiciona segmento final se necessário
if end_times and end_times[-1] < video_duration:
nonsilent_ranges.append((end_times[-1], video_duration))
return nonsilent_ranges
def get_video_duration(video_path):
"""Obtém a duração do vídeo usando FFmpeg"""
command = [
'ffprobe', '-v', 'error',
'-show_entries', 'format=duration',
'-of', 'json',
video_path
]
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
data = json.loads(result.stdout)
return float(data['format']['duration'])
def create_filter_complex(ranges):
"""Cria o filtro complexo para FFmpeg baseado nos intervalos não silenciosos"""
parts = []
for i, (start, end) in enumerate(ranges):
parts.append(f"[0:v]trim=start={start}:end={end},setpts=PTS-STARTPTS[v{i}]; "
f"[0:a]atrim=start={start}:end={end},asetpts=PTS-STARTPTS[a{i}]")
# Concatena os vídeos
v_list = ''.join(f'[v{i}]' for i in range(len(ranges)))
a_list = ''.join(f'[a{i}]' for i in range(len(ranges)))
concat = f"; {v_list}concat=n={len(ranges)}:v=1:a=0[vout]; "
concat += f"{a_list}concat=n={len(ranges)}:v=0:a=1[aout]"
return ''.join(parts) + concat
def process_video_fast(video_path, silence_thresh=-40, min_silence_len=1):
"""Processa o vídeo removendo silêncio usando FFmpeg diretamente"""
# Detecta intervalos não silenciosos
nonsilent_ranges = detect_silence_ffmpeg(video_path, silence_thresh, min_silence_len)
if not nonsilent_ranges:
return video_path
# Cria arquivo de saída
output_path = str(Path(video_path).parent / f"processed_{Path(video_path).name}")
# Cria filtro complexo
filter_complex = create_filter_complex(nonsilent_ranges)
# Processa o vídeo em uma única passagem
command = [
'ffmpeg', '-i', video_path,
'-filter_complex', filter_complex,
'-map', '[vout]',
'-map', '[aout]',
'-c:v', 'libx264',
'-preset', 'ultrafast', # Mais rápido encoding
'-c:a', 'aac',
'-y',
output_path
]
subprocess.run(command, stderr=subprocess.PIPE)
return output_path
def remove_silence(video_input, silence_duration, silence_threshold):
"""Função para remoção normal de silêncio"""
try:
if video_input is None:
raise ValueError("Por favor, faça upload de um vídeo")
return process_video_fast(
video_input,
silence_thresh=silence_threshold,
min_silence_len=silence_duration
)
except Exception as e:
gr.Error(str(e))
return None
def remove_max_silence(video_input):
"""Função 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")
return process_video_fast(
video_input,
silence_thresh=-30,
min_silence_len=0.1
)
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)