File size: 5,620 Bytes
ab496c0
 
 
 
 
 
d83a342
 
 
 
ab496c0
d83a342
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ab496c0
d83a342
 
 
 
 
 
 
 
 
 
 
 
 
ab496c0
d83a342
 
 
ab496c0
 
d83a342
ab496c0
d83a342
ab496c0
 
d83a342
ab496c0
 
 
 
 
 
 
 
 
d83a342
ab496c0
 
d83a342
 
 
 
 
 
 
 
 
 
 
ab496c0
d83a342
ab496c0
d83a342
ab496c0
d83a342
 
 
ab496c0
d83a342
 
 
 
ab496c0
 
 
 
 
 
d83a342
 
 
 
 
 
 
 
ab496c0
 
 
 
d83a342
ab496c0
 
d83a342
 
 
 
 
ab496c0
 
 
d83a342
 
 
 
ab496c0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
import gradio as gr
import moviepy.editor as mp
from pydub import AudioSegment
from pydub.silence import detect_nonsilent
import tempfile
import os
import subprocess
from concurrent.futures import ThreadPoolExecutor
import shutil
from pathlib import Path

def extract_audio_ffmpeg(video_path, output_path):
    """Extrai áudio usando FFmpeg diretamente para maior velocidade"""
    command = [
        'ffmpeg', '-i', video_path,
        '-vn',  # Pula o vídeo
        '-acodec', 'pcm_s16le',  # Formato de áudio
        '-ar', '44100',  # Sample rate
        '-ac', '2',  # Canais
        '-y',  # Sobrescreve arquivo se existir
        output_path
    ]
    subprocess.run(command, stderr=subprocess.PIPE)

def cut_video_ffmpeg(input_path, output_path, start, end):
    """Corta vídeo usando FFmpeg diretamente"""
    command = [
        'ffmpeg', '-i', input_path,
        '-ss', str(start),
        '-t', str(end - start),
        '-c', 'copy',  # Usa codec copying para maior velocidade
        '-avoid_negative_ts', '1',
        '-y',
        output_path
    ]
    subprocess.run(command, stderr=subprocess.PIPE)

def process_video_chunk(args):
    """Processa um chunk do vídeo"""
    input_path, output_path, start, end = args
    cut_video_ffmpeg(input_path, output_path, start, end)
    return output_path

def concatenate_videos_ffmpeg(video_list, output_path):
    """Concatena vídeos usando FFmpeg"""
    # Cria arquivo de lista
    list_file = tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt')
    for video in video_list:
        list_file.write(f"file '{video}'\n")
    list_file.close()
    
    # Concatena usando FFmpeg
    command = [
        'ffmpeg', '-f', 'concat',
        '-safe', '0',
        '-i', list_file.name,
        '-c', 'copy',
        '-y',
        output_path
    ]
    subprocess.run(command, stderr=subprocess.PIPE)
    os.unlink(list_file.name)

def process_video(video_path, min_silence_len=1000, silence_thresh=-40, max_workers=4):
    """
    Remove segmentos silenciosos do vídeo com processamento otimizado.
    """
    # Criar diretório temporário
    temp_dir = tempfile.mkdtemp()
    
    # Extrair áudio
    temp_audio = os.path.join(temp_dir, "temp_audio.wav")
    extract_audio_ffmpeg(video_path, temp_audio)
    
    # Detectar segmentos não silenciosos
    audio = AudioSegment.from_wav(temp_audio)
    nonsilent_ranges = detect_nonsilent(
        audio,
        min_silence_len=min_silence_len,
        silence_thresh=silence_thresh
    )
    
    if not nonsilent_ranges:
        return video_path
    
    # Converter para segundos
    nonsilent_ranges_sec = [(start/1000.0, end/1000.0) for start, end in nonsilent_ranges]
    
    # Preparar argumentos para processamento paralelo
    chunk_args = []
    chunk_outputs = []
    for i, (start, end) in enumerate(nonsilent_ranges_sec):
        output_chunk = os.path.join(temp_dir, f"chunk_{i}.mp4")
        chunk_args.append((video_path, output_chunk, start, end))
        chunk_outputs.append(output_chunk)
    
    # Processar chunks em paralelo
    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        list(executor.map(process_video_chunk, chunk_args))
    
    # Concatenar todos os chunks
    output_path = os.path.join(temp_dir, "processed_video.mp4")
    concatenate_videos_ffmpeg(chunk_outputs, output_path)
    
    # Criar cópia do resultado em local permanente
    final_output = str(Path(video_path).parent / f"processed_{Path(video_path).name}")
    shutil.copy2(output_path, final_output)
    
    # Limpar arquivos temporários
    shutil.rmtree(temp_dir)
    
    return final_output

def remove_silence(video, silence_duration, silence_threshold):
    if video is None:
        return None
    
    try:
        with gr.Progress() as progress:
            progress(0, desc="Iniciando processamento...")
            processed_video = process_video(
                video,
                min_silence_len=int(silence_duration * 1000),
                silence_thresh=silence_threshold
            )
            progress(1, desc="Processamento concluído!")
        return processed_video
    except Exception as e:
        return str(e)

# Interface Gradio com indicador de progresso
with gr.Blocks(title="Removedor de Silêncio de Vídeos") as app:
    gr.Markdown("# Removedor de Silêncio de Vídeos")
    gr.Markdown("""
    ### Otimizado para processamento rápido
    Faça upload de um vídeo e ajuste os parâmetros para remover segmentos silenciosos.
    O processamento é feito em paralelo para maior velocidade.
    """)
    
    with gr.Row():
        with gr.Column():
            video_input = gr.Video(
                label="Vídeo de Entrada",
                type="filepath"  # Usar filepath para upload mais rápido
            )
            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)"
            )
            process_btn = gr.Button("Processar Vídeo")
        
        with gr.Column():
            video_output = gr.Video(label="Vídeo Processado")
    
    process_btn.click(
        fn=remove_silence,
        inputs=[video_input, silence_duration, silence_threshold],
        outputs=video_output
    )

if __name__ == "__main__":
    app.launch()