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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +59 -230
app.py CHANGED
@@ -1,253 +1,82 @@
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', # Используем 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
- # Убрал декоратор @spaces.GPU - у Space нет GPU
135
- async def finalise_video(start_video):
136
- """Основная функция обработки видео"""
137
- job_id = str(uuid.uuid4())
138
- logger.info(f"Starting video processing job: {job_id}")
139
 
140
- try:
141
- # Обрабатываем видео
142
- output_path1, output_path2 = await process_video_with_resources(
143
- start_video, job_id
144
- )
145
-
146
- logger.info(f"Completed video processing job: {job_id}")
147
- return output_path1, output_path2
148
-
149
- except Exception as e:
150
- logger.error(f"Finalise video error: {e}")
151
- raise gr.Error(f"Ошибка обработки видео: {str(e)}")
152
 
153
  def check_ffmpeg():
154
- """Проверка доступности ffmpeg"""
155
  try:
156
- result = subprocess.run(
157
- ['ffmpeg', '-version'],
158
- capture_output=True,
159
- text=True,
160
- timeout=5
161
- )
162
- return result.returncode == 0
163
- except Exception:
164
  return False
165
 
166
- def cleanup_old_files():
167
- """Фоновая очистка старых файлов"""
168
- try:
169
- now = asyncio.get_event_loop().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 = now - 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
- async def periodic_cleanup():
185
- """Периодическая очистка старых файлов"""
186
- while True:
187
- await asyncio.sleep(3600) # Каждый час
188
- cleanup_old_files()
189
-
190
- # Создаем интерфейс Gradio
191
- with gr.Blocks(theme=gr.themes.Soft()) as demo:
192
- gr.Markdown("# Видео Финализатор")
193
-
194
- with gr.Row():
195
- with gr.Column(scale=1):
196
- input_video = gr.Video(
197
- label="Входное видео",
198
- interactive=True
199
- )
200
-
201
- process_btn = gr.Button(
202
- "Обработать видео",
203
- variant="primary",
204
- size="lg"
205
- )
206
-
207
- with gr.Column(scale=2):
208
- with gr.Row():
209
- output1 = gr.Video(
210
- label="Видео с аудио",
211
- autoplay=True,
212
- interactive=False
213
- )
214
- output2 = gr.Video(
215
- label="Размытое видео",
216
- autoplay=True,
217
- interactive=False
218
- )
219
-
220
- # Обработчик без ограничений параллелизма
221
- process_btn.click(
222
- fn=finalise_video,
223
- inputs=[input_video],
224
- outputs=[output1, output2]
225
- )
226
 
227
  if __name__ == "__main__":
228
- # Проверяем ffmpeg
229
- if not check_ffmpeg():
230
- logger.error("FFmpeg не найден! Установите ffmpeg.")
231
- print("Ошибка: FFmpeg не установлен.")
232
- print("Для установки в Hugging Face Spaces добавьте в README.md:")
233
- print("```")
234
- print("app_port: 7860")
235
- print("sdk: gradio")
236
- print("sdk_version: 4.0.0")
237
- print("```")
238
- print("И убедитесь, что ffmpeg установлен в системе.")
239
-
240
- # Запускаем с максимальной производительностью
241
- # Нет ограничений на параллелизм - будьте осторожны!
242
- demo.queue(
243
- max_size=100, # Большая очередь
244
- default_concurrency_limit=None # Без ограничений
245
- ).launch(
246
- server_name="0.0.0.0",
247
- server_port=7860,
248
- share=False,
249
- max_threads=100, # Много потоков для обработки
250
- enable_queue=True,
251
- show_error=True,
252
- debug=False
253
- )
 
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)