GiorgioV commited on
Commit
2739cae
·
verified ·
1 Parent(s): 0294c78

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +221 -59
app.py CHANGED
@@ -1,82 +1,244 @@
1
  import spaces
2
  import gradio as gr
3
  import tempfile
4
- import numpy as np
5
  import os
6
  import subprocess
7
  import shutil
8
- import ffmpeg
 
 
 
9
 
10
- def finalise_video(start_video):
11
- if check_ffmpeg():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  try:
13
- with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as video_tmpfile:
14
- if isinstance(start_video, str):
15
- shutil.copy(start_video, video_tmpfile.name)
16
- else:
17
- start_video.save(video_tmpfile.name, format="MP4")
18
- video_path = video_tmpfile.name
19
 
20
- with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as audio_tmpfile:
21
- video_with_audio_path = audio_tmpfile.name
22
- cmd = [
23
- 'ffmpeg',
24
- '-f', 'lavfi',
25
- '-i', 'anullsrc=channel_layout=stereo:sample_rate=44100',
26
- '-i', video_path,
27
- '-c:v', 'copy',
28
- '-c:a', 'aac',
29
- '-shortest',
30
- '-y',
31
- video_with_audio_path
32
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
- subprocess.run(cmd, capture_output=True, check=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
35
 
36
- with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as blur_tmpfile:
37
- blurred_video_path = blur_tmpfile.name
38
 
39
- cmd_blur = [
40
- 'ffmpeg',
41
- '-i', video_with_audio_path, # Используем видео с аудио как источник
42
- '-vf', 'gblur=sigma=25', # Гауссово размытие с sigma=5
43
- '-c:a', 'copy', # Копируем аудио без изменений
44
- '-y',
45
- blurred_video_path
46
- ]
 
 
 
47
 
48
- subprocess.run(cmd_blur, capture_output=True, check=True)
49
 
50
- os.unlink(video_path)
 
51
 
52
- return video_with_audio_path, blurred_video_path
53
- except Exception as e:
54
- print(f"Error finaliser: {e}")
55
- return
56
- else:
57
- print("FFmpeg not available, returning video without audio")
58
- return video_path, video_path, current_seed
59
-
60
- with gr.Blocks() as demo:
61
- gr.Markdown("Finaliser")
62
- with gr.Row():
63
- with gr.Column():
64
- input_video_component = gr.Video(label="Input Video")
65
- generate_button = gr.Button("Finalise", variant="primary")
66
- with gr.Column():
67
- video_output_1 = gr.Video(label="Generated Video", autoplay=True, interactive=False)
68
- video_output_2 = gr.Video(label="Generated Video", autoplay=True, interactive=False)
 
 
 
 
 
69
 
70
- ui_inputs = [input_video_component]
71
- generate_button.click(fn=finalise_video, inputs=ui_inputs, outputs=[video_output_1, video_output_2])
 
 
 
 
 
 
 
 
 
 
72
 
73
  def check_ffmpeg():
 
74
  try:
75
- subprocess.run(['ffmpeg', '-version'], capture_output=True, check=True)
76
- return True
77
- except (subprocess.CalledProcessError, FileNotFoundError):
 
 
 
 
 
78
  return False
79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
 
81
  if __name__ == "__main__":
82
- demo.queue().launch(mcp_server=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import spaces
2
  import gradio as gr
3
  import tempfile
 
4
  import os
5
  import subprocess
6
  import shutil
7
+ import asyncio
8
+ from pathlib import Path
9
+ import uuid
10
+ import logging
11
 
12
+ # Настройка логирования
13
+ logging.basicConfig(level=logging.INFO)
14
+ logger = logging.getLogger(__name__)
15
+
16
+ # Конфигурация
17
+ MAX_PROCESS_TIMEOUT = 120
18
+ TEMP_BASE_DIR = Path("/tmp/video_processing")
19
+ TEMP_BASE_DIR.mkdir(exist_ok=True, parents=True)
20
+
21
+ async def run_ffmpeg_async(cmd, timeout=120):
22
+ """Асинхронный запуск ffmpeg с таймаутом"""
23
+ try:
24
+ process = await asyncio.create_subprocess_exec(
25
+ *cmd,
26
+ stdout=asyncio.subprocess.PIPE,
27
+ stderr=asyncio.subprocess.PIPE
28
+ )
29
+
30
  try:
31
+ stdout, stderr = await asyncio.wait_for(
32
+ process.communicate(),
33
+ timeout=timeout
34
+ )
 
 
35
 
36
+ if process.returncode != 0:
37
+ error_msg = stderr.decode() if stderr else "Unknown error"
38
+ logger.error(f"FFmpeg error: {error_msg}")
39
+ raise Exception(f"FFmpeg failed: {error_msg}")
40
+
41
+ return stdout, stderr
42
+
43
+ except asyncio.TimeoutError:
44
+ try:
45
+ process.kill()
46
+ except:
47
+ pass
48
+ raise Exception(f"FFmpeg timeout after {timeout} seconds")
49
+
50
+ except Exception as e:
51
+ logger.error(f"FFmpeg execution error: {e}")
52
+ raise
53
+
54
+ async def process_video_with_resources(start_video, job_id):
55
+ """Обработка видео с изоляцией ресурсов"""
56
+ # Создаем уникальную рабочую директорию для каждого задания
57
+ work_dir = TEMP_BASE_DIR / job_id
58
+ work_dir.mkdir(exist_ok=True)
59
+
60
+ # Пути к файлам
61
+ input_path = work_dir / "input.mp4"
62
+ audio_path = work_dir / "with_audio.mp4"
63
+ blurred_path = work_dir / "blurred.mp4"
64
+
65
+ try:
66
+ # Шаг 1: Сохраняем входное видео
67
+ if isinstance(start_video, str):
68
+ shutil.copy(start_video, str(input_path))
69
+ else:
70
+ # Для объектов Gradio Video
71
+ with open(input_path, "wb") as f:
72
+ # Gradio Video возвращает временный файл
73
+ if hasattr(start_video, "name"):
74
+ with open(start_video.name, "rb") as src:
75
+ f.write(src.read())
76
+ else:
77
+ # Fallback
78
+ import io
79
+ if isinstance(start_video, io.IOBase):
80
+ f.write(start_video.read())
81
 
82
+ # Шаг 2: Добавляем аудио
83
+ cmd_add_audio = [
84
+ 'ffmpeg',
85
+ '-f', 'lavfi',
86
+ '-i', 'anullsrc=channel_layout=stereo:sample_rate=44100',
87
+ '-i', str(input_path),
88
+ '-c:v', 'copy',
89
+ '-c:a', 'aac',
90
+ '-shortest',
91
+ '-threads', '2',
92
+ '-loglevel', 'error',
93
+ '-y',
94
+ str(audio_path)
95
+ ]
96
 
97
+ await run_ffmpeg_async(cmd_add_audio, timeout=MAX_PROCESS_TIMEOUT)
 
98
 
99
+ # Шаг 3: Применяем размытие
100
+ cmd_blur = [
101
+ 'ffmpeg',
102
+ '-i', str(audio_path),
103
+ '-vf', 'gblur=sigma=25',
104
+ '-c:a', 'copy',
105
+ '-threads', '2',
106
+ '-loglevel', 'error',
107
+ '-y',
108
+ str(blurred_path)
109
+ ]
110
 
111
+ await run_ffmpeg_async(cmd_blur, timeout=MAX_PROCESS_TIMEOUT)
112
 
113
+ # Возвращаем пути к результатам
114
+ return str(audio_path), str(blurred_path)
115
 
116
+ except Exception as e:
117
+ logger.error(f"Processing error for job {job_id}: {e}")
118
+
119
+ # Удаляем рабочую директорию в случае ошибки
120
+ try:
121
+ shutil.rmtree(work_dir)
122
+ except:
123
+ pass
124
+
125
+ raise
126
+ finally:
127
+ # Удаляем только входной файл, результаты остаются
128
+ try:
129
+ if input_path.exists():
130
+ input_path.unlink()
131
+ except:
132
+ pass
133
+
134
+ async def finalise_video(start_video):
135
+ """Основная функция обработки видео"""
136
+ job_id = str(uuid.uuid4())
137
+ logger.info(f"Starting video processing job: {job_id}")
138
 
139
+ try:
140
+ # Обрабатываем видео
141
+ output_path1, output_path2 = await process_video_with_resources(
142
+ start_video, job_id
143
+ )
144
+
145
+ logger.info(f"Completed video processing job: {job_id}")
146
+ return output_path1, output_path2
147
+
148
+ except Exception as e:
149
+ logger.error(f"Finalise video error: {e}")
150
+ raise gr.Error(f"Ошибка обработки видео: {str(e)}")
151
 
152
  def check_ffmpeg():
153
+ """Проверка доступности ffmpeg"""
154
  try:
155
+ result = subprocess.run(
156
+ ['ffmpeg', '-version'],
157
+ capture_output=True,
158
+ text=True,
159
+ timeout=5
160
+ )
161
+ return result.returncode == 0
162
+ except Exception:
163
  return False
164
 
165
+ def cleanup_old_files():
166
+ """Фоновая очистка старых файлов"""
167
+ try:
168
+ import time
169
+ current_time = time.time()
170
+ for item in TEMP_BASE_DIR.iterdir():
171
+ try:
172
+ if item.is_dir():
173
+ # Удаляем директории старше 1 часа
174
+ stat = item.stat()
175
+ dir_age = current_time - stat.st_ctime
176
+ if dir_age > 3600: # 1 час
177
+ shutil.rmtree(item, ignore_errors=True)
178
+ logger.info(f"Cleaned up old directory: {item}")
179
+ except Exception as e:
180
+ logger.warning(f"Failed to clean up {item}: {e}")
181
+ except Exception as e:
182
+ logger.error(f"Cleanup error: {e}")
183
+
184
+ # Создаем интерфейс Gradio
185
+ # ИСПРАВЛЕНИЕ: убрали theme из конструктора Blocks
186
+ with gr.Blocks() as demo:
187
+ gr.Markdown("# Видео Финализатор")
188
+
189
+ with gr.Row():
190
+ with gr.Column(scale=1):
191
+ input_video = gr.Video(
192
+ label="Входное видео",
193
+ interactive=True
194
+ )
195
+
196
+ process_btn = gr.Button(
197
+ "Обработать видео",
198
+ variant="primary"
199
+ )
200
+
201
+ with gr.Column(scale=2):
202
+ with gr.Row():
203
+ output1 = gr.Video(
204
+ label="Видео с аудио",
205
+ autoplay=True,
206
+ interactive=False
207
+ )
208
+ output2 = gr.Video(
209
+ label="Размытое видео",
210
+ autoplay=True,
211
+ interactive=False
212
+ )
213
+
214
+ # Обработчик без ограничений параллелизма
215
+ process_btn.click(
216
+ fn=finalise_video,
217
+ inputs=[input_video],
218
+ outputs=[output1, output2]
219
+ )
220
 
221
  if __name__ == "__main__":
222
+ # Проверяем ffmpeg
223
+ if not check_ffmpeg():
224
+ logger.error("FFmpeg не найден! Установите ffmpeg.")
225
+ print("Ошибка: FFmpeg не установлен.")
226
+
227
+ # Периодическая очистка старых файлов
228
+ import threading
229
+ import time
230
+
231
+ def cleanup_thread():
232
+ while True:
233
+ cleanup_old_files()
234
+ time.sleep(3600) # Каждый час
235
+
236
+ cleanup_thread = threading.Thread(target=cleanup_thread, daemon=True)
237
+ cleanup_thread.start()
238
+
239
+ # ИСПРАВЛЕНИЕ: правильный запуск для Gradio 4.0+
240
+ # Убраны несовместимые параметры: enable_queue, max_threads
241
+ demo.queue(max_size=100).launch(
242
+ server_name="0.0.0.0",
243
+ server_port=7860
244
+ )