File size: 8,134 Bytes
7270299 2aed453 60892a9 2739cae 7270299 2739cae a119648 2739cae 0294c78 2739cae a119648 2739cae a119648 2739cae a119648 2739cae a119648 2739cae cdfac50 2739cae cdfac50 2739cae 0294c78 2739cae a119648 2739cae a119648 2739cae a119648 2739cae b3a1b8a 2739cae a119648 7270299 2739cae b3a1b8a 2739cae |
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 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
import spaces
import gradio as gr
import tempfile
import os
import subprocess
import shutil
import asyncio
from pathlib import Path
import uuid
import logging
# Настройка логирования
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Конфигурация
MAX_PROCESS_TIMEOUT = 120
TEMP_BASE_DIR = Path("/tmp/video_processing")
TEMP_BASE_DIR.mkdir(exist_ok=True, parents=True)
async def run_ffmpeg_async(cmd, timeout=120):
"""Асинхронный запуск ffmpeg с таймаутом"""
try:
process = await asyncio.create_subprocess_exec(
*cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
try:
stdout, stderr = await asyncio.wait_for(
process.communicate(),
timeout=timeout
)
if process.returncode != 0:
error_msg = stderr.decode() if stderr else "Unknown error"
logger.error(f"FFmpeg error: {error_msg}")
raise Exception(f"FFmpeg failed: {error_msg}")
return stdout, stderr
except asyncio.TimeoutError:
try:
process.kill()
except:
pass
raise Exception(f"FFmpeg timeout after {timeout} seconds")
except Exception as e:
logger.error(f"FFmpeg execution error: {e}")
raise
async def process_video_with_resources(start_video, job_id):
"""Обработка видео с изоляцией ресурсов"""
# Создаем уникальную рабочую директорию для каждого задания
work_dir = TEMP_BASE_DIR / job_id
work_dir.mkdir(exist_ok=True)
# Пути к файлам
input_path = work_dir / "input.mp4"
audio_path = work_dir / "with_audio.mp4"
blurred_path = work_dir / "blurred.mp4"
try:
# Шаг 1: Сохраняем входное видео
if isinstance(start_video, str):
shutil.copy(start_video, str(input_path))
else:
# Для объектов Gradio Video
with open(input_path, "wb") as f:
# Gradio Video возвращает временный файл
if hasattr(start_video, "name"):
with open(start_video.name, "rb") as src:
f.write(src.read())
else:
# Fallback
import io
if isinstance(start_video, io.IOBase):
f.write(start_video.read())
# Шаг 2: Добавляем аудио
cmd_add_audio = [
'ffmpeg',
'-f', 'lavfi',
'-i', 'anullsrc=channel_layout=stereo:sample_rate=44100',
'-i', str(input_path),
'-c:v', 'copy',
'-c:a', 'aac',
'-shortest',
'-threads', '2',
'-loglevel', 'error',
'-y',
str(audio_path)
]
await run_ffmpeg_async(cmd_add_audio, timeout=MAX_PROCESS_TIMEOUT)
# Шаг 3: Применяем размытие
cmd_blur = [
'ffmpeg',
'-i', str(audio_path),
'-vf', 'gblur=sigma=25',
'-c:a', 'copy',
'-threads', '2',
'-loglevel', 'error',
'-y',
str(blurred_path)
]
await run_ffmpeg_async(cmd_blur, timeout=MAX_PROCESS_TIMEOUT)
# Возвращаем пути к результатам
return str(audio_path), str(blurred_path)
except Exception as e:
logger.error(f"Processing error for job {job_id}: {e}")
# Удаляем рабочую директорию в случае ошибки
try:
shutil.rmtree(work_dir)
except:
pass
raise
finally:
# Удаляем только входной файл, результаты остаются
try:
if input_path.exists():
input_path.unlink()
except:
pass
async def finalise_video(start_video):
"""Основная функция обработки видео"""
job_id = str(uuid.uuid4())
logger.info(f"Starting video processing job: {job_id}")
try:
# Обрабатываем видео
output_path1, output_path2 = await process_video_with_resources(
start_video, job_id
)
logger.info(f"Completed video processing job: {job_id}")
return output_path1, output_path2
except Exception as e:
logger.error(f"Finalise video error: {e}")
raise gr.Error(f"Ошибка обработки видео: {str(e)}")
def check_ffmpeg():
"""Проверка доступности ffmpeg"""
try:
result = subprocess.run(
['ffmpeg', '-version'],
capture_output=True,
text=True,
timeout=5
)
return result.returncode == 0
except Exception:
return False
def cleanup_old_files():
"""Фоновая очистка старых файлов"""
try:
import time
current_time = time.time()
for item in TEMP_BASE_DIR.iterdir():
try:
if item.is_dir():
# Удаляем директории старше 1 часа
stat = item.stat()
dir_age = current_time - stat.st_ctime
if dir_age > 600:
shutil.rmtree(item, ignore_errors=True)
logger.info(f"Cleaned up old directory: {item}")
except Exception as e:
logger.warning(f"Failed to clean up {item}: {e}")
except Exception as e:
logger.error(f"Cleanup error: {e}")
# Создаем интерфейс Gradio
# ИСПРАВЛЕНИЕ: убрали theme из конструктора Blocks
with gr.Blocks() as demo:
gr.Markdown("# Видео Финализатор")
with gr.Row():
with gr.Column(scale=1):
input_video = gr.Video(
label="Входное видео",
interactive=True
)
process_btn = gr.Button(
"Обработать видео",
variant="primary"
)
with gr.Column(scale=2):
with gr.Row():
output1 = gr.Video(
label="Видео с аудио",
autoplay=True,
interactive=False
)
output2 = gr.Video(
label="Размытое видео",
autoplay=True,
interactive=False
)
# Обработчик без ограничений параллелизма
process_btn.click(
fn=finalise_video,
inputs=[input_video],
outputs=[output1, output2]
)
if __name__ == "__main__":
# Проверяем ffmpeg
if not check_ffmpeg():
logger.error("FFmpeg не найден! Установите ffmpeg.")
print("Ошибка: FFmpeg не установлен.")
# Периодическая очистка старых файлов
import threading
import time
def cleanup_thread():
while True:
cleanup_old_files()
time.sleep(600) # Каждый час
cleanup_thread = threading.Thread(target=cleanup_thread, daemon=True)
cleanup_thread.start()
# ИСПРАВЛЕНИЕ: правильный запуск для Gradio 4.0+
# Убраны несовместимые параметры: enable_queue, max_threads
demo.queue(max_size=100).launch(
server_name="0.0.0.0",
server_port=7860
) |