Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -43,13 +43,11 @@ def process_video_chunk(args):
|
|
43 |
|
44 |
def concatenate_videos_ffmpeg(video_list, output_path):
|
45 |
"""Concatena vídeos usando FFmpeg"""
|
46 |
-
# Cria arquivo de lista
|
47 |
list_file = tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt')
|
48 |
for video in video_list:
|
49 |
list_file.write(f"file '{video}'\n")
|
50 |
list_file.close()
|
51 |
|
52 |
-
# Concatena usando FFmpeg
|
53 |
command = [
|
54 |
'ffmpeg', '-f', 'concat',
|
55 |
'-safe', '0',
|
@@ -62,21 +60,16 @@ def concatenate_videos_ffmpeg(video_list, output_path):
|
|
62 |
os.unlink(list_file.name)
|
63 |
|
64 |
def process_video(video_path, min_silence_len=1000, silence_thresh=-40, max_workers=4):
|
65 |
-
"""
|
66 |
-
Remove segmentos silenciosos do vídeo com processamento otimizado.
|
67 |
-
"""
|
68 |
if not os.path.exists(video_path):
|
69 |
raise ValueError("Arquivo de vídeo não encontrado")
|
70 |
|
71 |
-
# Criar diretório temporário
|
72 |
temp_dir = tempfile.mkdtemp()
|
73 |
|
74 |
try:
|
75 |
-
# Extrair áudio
|
76 |
temp_audio = os.path.join(temp_dir, "temp_audio.wav")
|
77 |
extract_audio_ffmpeg(video_path, temp_audio)
|
78 |
|
79 |
-
# Detectar segmentos não silenciosos
|
80 |
audio = AudioSegment.from_wav(temp_audio)
|
81 |
nonsilent_ranges = detect_nonsilent(
|
82 |
audio,
|
@@ -87,10 +80,8 @@ def process_video(video_path, min_silence_len=1000, silence_thresh=-40, max_work
|
|
87 |
if not nonsilent_ranges:
|
88 |
return video_path
|
89 |
|
90 |
-
# Converter para segundos
|
91 |
nonsilent_ranges_sec = [(start/1000.0, end/1000.0) for start, end in nonsilent_ranges]
|
92 |
|
93 |
-
# Preparar argumentos para processamento paralelo
|
94 |
chunk_args = []
|
95 |
chunk_outputs = []
|
96 |
for i, (start, end) in enumerate(nonsilent_ranges_sec):
|
@@ -98,15 +89,12 @@ def process_video(video_path, min_silence_len=1000, silence_thresh=-40, max_work
|
|
98 |
chunk_args.append((video_path, output_chunk, start, end))
|
99 |
chunk_outputs.append(output_chunk)
|
100 |
|
101 |
-
# Processar chunks em paralelo
|
102 |
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
103 |
list(executor.map(process_video_chunk, chunk_args))
|
104 |
|
105 |
-
# Concatenar todos os chunks
|
106 |
output_path = os.path.join(temp_dir, "processed_video.mp4")
|
107 |
concatenate_videos_ffmpeg(chunk_outputs, output_path)
|
108 |
|
109 |
-
# Criar cópia do resultado em local permanente
|
110 |
final_output = str(Path(video_path).parent / f"processed_{Path(video_path).name}")
|
111 |
shutil.copy2(output_path, final_output)
|
112 |
|
@@ -115,95 +103,96 @@ def process_video(video_path, min_silence_len=1000, silence_thresh=-40, max_work
|
|
115 |
except Exception as e:
|
116 |
raise Exception(f"Erro ao processar vídeo: {str(e)}")
|
117 |
finally:
|
118 |
-
# Limpar arquivos temporários
|
119 |
shutil.rmtree(temp_dir)
|
120 |
|
121 |
def remove_silence(video_input, silence_duration, silence_threshold):
|
122 |
-
"""
|
123 |
-
Função principal que processa o vídeo e retorna o caminho do vídeo processado
|
124 |
-
"""
|
125 |
try:
|
126 |
if video_input is None:
|
127 |
raise ValueError("Por favor, faça upload de um vídeo")
|
128 |
|
129 |
-
# Processar o vídeo
|
130 |
processed_video = process_video(
|
131 |
video_input,
|
132 |
min_silence_len=int(silence_duration * 1000),
|
133 |
silence_thresh=silence_threshold
|
134 |
)
|
135 |
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
141 |
|
|
|
142 |
except Exception as e:
|
143 |
gr.Error(str(e))
|
144 |
return None
|
145 |
|
146 |
-
# Interface Gradio
|
147 |
with gr.Blocks(title="Removedor de Silêncio de Vídeos") as app:
|
148 |
gr.Markdown("# Removedor de Silêncio de Vídeos")
|
149 |
-
gr.Markdown("""
|
150 |
-
### Otimizado para processamento rápido
|
151 |
-
Faça upload de um vídeo e ajuste os parâmetros para remover segmentos silenciosos.
|
152 |
-
O processamento é feito em paralelo para maior velocidade.
|
153 |
-
""")
|
154 |
|
155 |
with gr.Row():
|
156 |
with gr.Column():
|
157 |
-
# Input components
|
158 |
video_input = gr.Video(
|
159 |
label="Selecione ou Arraste o Vídeo",
|
160 |
-
format="mp4"
|
161 |
-
interactive=True
|
162 |
-
)
|
163 |
-
silence_duration = gr.Slider(
|
164 |
-
minimum=0.1,
|
165 |
-
maximum=5.0,
|
166 |
-
value=1.0,
|
167 |
-
step=0.1,
|
168 |
-
label="Duração Mínima do Silêncio (segundos)"
|
169 |
-
)
|
170 |
-
silence_threshold = gr.Slider(
|
171 |
-
minimum=-60,
|
172 |
-
maximum=-20,
|
173 |
-
value=-40,
|
174 |
-
step=1,
|
175 |
-
label="Limite de Silêncio (dB)"
|
176 |
)
|
177 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
178 |
|
179 |
with gr.Row():
|
180 |
-
|
181 |
-
# Display do vídeo original
|
182 |
-
gr.Markdown("### Vídeo Original")
|
183 |
-
video_input_display = gr.Video(label="Vídeo Original")
|
184 |
-
with gr.Column():
|
185 |
-
# Display do vídeo processado
|
186 |
-
gr.Markdown("### Vídeo Processado (Sem Silêncio)")
|
187 |
-
video_output = gr.Video(label="Vídeo Processado")
|
188 |
|
189 |
# Event handlers
|
190 |
-
|
191 |
-
fn=
|
192 |
inputs=[video_input],
|
193 |
-
outputs=[
|
194 |
)
|
195 |
|
196 |
-
|
197 |
fn=remove_silence,
|
198 |
inputs=[
|
199 |
video_input,
|
200 |
silence_duration,
|
201 |
silence_threshold
|
202 |
],
|
203 |
-
outputs=[
|
204 |
-
video_output,
|
205 |
-
video_input_display
|
206 |
-
]
|
207 |
)
|
208 |
|
209 |
if __name__ == "__main__":
|
|
|
43 |
|
44 |
def concatenate_videos_ffmpeg(video_list, output_path):
|
45 |
"""Concatena vídeos usando FFmpeg"""
|
|
|
46 |
list_file = tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt')
|
47 |
for video in video_list:
|
48 |
list_file.write(f"file '{video}'\n")
|
49 |
list_file.close()
|
50 |
|
|
|
51 |
command = [
|
52 |
'ffmpeg', '-f', 'concat',
|
53 |
'-safe', '0',
|
|
|
60 |
os.unlink(list_file.name)
|
61 |
|
62 |
def process_video(video_path, min_silence_len=1000, silence_thresh=-40, max_workers=4):
|
63 |
+
"""Remove segmentos silenciosos do vídeo com processamento otimizado."""
|
|
|
|
|
64 |
if not os.path.exists(video_path):
|
65 |
raise ValueError("Arquivo de vídeo não encontrado")
|
66 |
|
|
|
67 |
temp_dir = tempfile.mkdtemp()
|
68 |
|
69 |
try:
|
|
|
70 |
temp_audio = os.path.join(temp_dir, "temp_audio.wav")
|
71 |
extract_audio_ffmpeg(video_path, temp_audio)
|
72 |
|
|
|
73 |
audio = AudioSegment.from_wav(temp_audio)
|
74 |
nonsilent_ranges = detect_nonsilent(
|
75 |
audio,
|
|
|
80 |
if not nonsilent_ranges:
|
81 |
return video_path
|
82 |
|
|
|
83 |
nonsilent_ranges_sec = [(start/1000.0, end/1000.0) for start, end in nonsilent_ranges]
|
84 |
|
|
|
85 |
chunk_args = []
|
86 |
chunk_outputs = []
|
87 |
for i, (start, end) in enumerate(nonsilent_ranges_sec):
|
|
|
89 |
chunk_args.append((video_path, output_chunk, start, end))
|
90 |
chunk_outputs.append(output_chunk)
|
91 |
|
|
|
92 |
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
93 |
list(executor.map(process_video_chunk, chunk_args))
|
94 |
|
|
|
95 |
output_path = os.path.join(temp_dir, "processed_video.mp4")
|
96 |
concatenate_videos_ffmpeg(chunk_outputs, output_path)
|
97 |
|
|
|
98 |
final_output = str(Path(video_path).parent / f"processed_{Path(video_path).name}")
|
99 |
shutil.copy2(output_path, final_output)
|
100 |
|
|
|
103 |
except Exception as e:
|
104 |
raise Exception(f"Erro ao processar vídeo: {str(e)}")
|
105 |
finally:
|
|
|
106 |
shutil.rmtree(temp_dir)
|
107 |
|
108 |
def remove_silence(video_input, silence_duration, silence_threshold):
|
109 |
+
"""Função para remoção normal de silêncio"""
|
|
|
|
|
110 |
try:
|
111 |
if video_input is None:
|
112 |
raise ValueError("Por favor, faça upload de um vídeo")
|
113 |
|
|
|
114 |
processed_video = process_video(
|
115 |
video_input,
|
116 |
min_silence_len=int(silence_duration * 1000),
|
117 |
silence_thresh=silence_threshold
|
118 |
)
|
119 |
|
120 |
+
return processed_video
|
121 |
+
except Exception as e:
|
122 |
+
gr.Error(str(e))
|
123 |
+
return None
|
124 |
+
|
125 |
+
def remove_max_silence(video_input):
|
126 |
+
"""Função para remoção máxima de silêncio"""
|
127 |
+
try:
|
128 |
+
if video_input is None:
|
129 |
+
raise ValueError("Por favor, faça upload de um vídeo")
|
130 |
+
|
131 |
+
# Configurações mais agressivas para detectar todo silêncio
|
132 |
+
processed_video = process_video(
|
133 |
+
video_input,
|
134 |
+
min_silence_len=100, # Detecta silêncios de 0.1 segundos
|
135 |
+
silence_thresh=-30 # Limite mais alto para detectar mais silêncio
|
136 |
+
)
|
137 |
|
138 |
+
return processed_video
|
139 |
except Exception as e:
|
140 |
gr.Error(str(e))
|
141 |
return None
|
142 |
|
143 |
+
# Interface Gradio
|
144 |
with gr.Blocks(title="Removedor de Silêncio de Vídeos") as app:
|
145 |
gr.Markdown("# Removedor de Silêncio de Vídeos")
|
|
|
|
|
|
|
|
|
|
|
146 |
|
147 |
with gr.Row():
|
148 |
with gr.Column():
|
|
|
149 |
video_input = gr.Video(
|
150 |
label="Selecione ou Arraste o Vídeo",
|
151 |
+
format="mp4"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
152 |
)
|
153 |
+
|
154 |
+
with gr.Row():
|
155 |
+
# Botão para remoção máxima de silêncio
|
156 |
+
remove_max_btn = gr.Button("🔇 Remover 100% do Silêncio", variant="primary")
|
157 |
+
|
158 |
+
# Botão para remoção personalizada
|
159 |
+
remove_custom_btn = gr.Button("Remover Silêncio Personalizado")
|
160 |
+
|
161 |
+
with gr.Group():
|
162 |
+
gr.Markdown("### Configurações Personalizadas")
|
163 |
+
silence_duration = gr.Slider(
|
164 |
+
minimum=0.1,
|
165 |
+
maximum=5.0,
|
166 |
+
value=1.0,
|
167 |
+
step=0.1,
|
168 |
+
label="Duração Mínima do Silêncio (segundos)"
|
169 |
+
)
|
170 |
+
silence_threshold = gr.Slider(
|
171 |
+
minimum=-60,
|
172 |
+
maximum=-20,
|
173 |
+
value=-40,
|
174 |
+
step=1,
|
175 |
+
label="Limite de Silêncio (dB)"
|
176 |
+
)
|
177 |
|
178 |
with gr.Row():
|
179 |
+
video_output = gr.Video(label="Vídeo Processado")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
180 |
|
181 |
# Event handlers
|
182 |
+
remove_max_btn.click(
|
183 |
+
fn=remove_max_silence,
|
184 |
inputs=[video_input],
|
185 |
+
outputs=[video_output]
|
186 |
)
|
187 |
|
188 |
+
remove_custom_btn.click(
|
189 |
fn=remove_silence,
|
190 |
inputs=[
|
191 |
video_input,
|
192 |
silence_duration,
|
193 |
silence_threshold
|
194 |
],
|
195 |
+
outputs=[video_output]
|
|
|
|
|
|
|
196 |
)
|
197 |
|
198 |
if __name__ == "__main__":
|