Редактировать file_service.py

This commit is contained in:
Markov Andrey
2026-06-30 21:25:32 +00:00
parent e4dc5611a6
commit 3e2f48fc1b

View File

@@ -4,6 +4,7 @@
хирургическая замена в DOCX, создание DOCX из текста,
транскрипция аудио через SaluteSpeech.
Все методы, которые могут быть асинхронными, теперь async.
Добавлено экранирование путей через shlex.quote.
"""
import asyncio
@@ -13,6 +14,7 @@ import logging
import os
import re
import shutil
import shlex
import subprocess
import tempfile
import zipfile
@@ -81,8 +83,6 @@ class FileService:
# ---------- Синхронные методы извлечения (вызываются через to_thread) ----------
def extract_text_from_docx(self, file_path: str) -> str:
"""Извлекает текст из DOCX, включая колонтитулы, таблицы и сноски."""
# ... (код без изменений, полная версия была ранее)
# Я приведу полный код этого метода, чтобы сохранить целостность.
try:
doc = DocxReader(file_path)
full_text = []
@@ -235,6 +235,7 @@ class FileService:
"""
Асинхронно транскрибирует аудиофайл через SaluteSpeech API.
Все блокирующие вызовы (subprocess.run, requests.post) обёрнуты в asyncio.to_thread.
Пути экранируются через shlex.quote.
"""
import uuid
@@ -247,14 +248,17 @@ class FileService:
if not shutil.which('ffmpeg') and not os.path.exists(ffmpeg_bin):
return "[Ошибка: на сервере не установлен ffmpeg]"
# Экранируем пути
safe_file_path = shlex.quote(file_path)
pcm_output = file_path + ".raw"
safe_pcm_output = shlex.quote(pcm_output)
try:
# Конвертация ffmpeg
def _run_ffmpeg():
cmd_ffmpeg = [
ffmpeg_bin, '-y', '-i', file_path,
'-ac', '1', '-ar', '16000', '-f', 's16le', '-filter:a', 'volume=2.0', pcm_output
ffmpeg_bin, '-y', '-i', safe_file_path,
'-ac', '1', '-ar', '16000', '-f', 's16le', '-filter:a', 'volume=2.0', safe_pcm_output
]
return subprocess.run(cmd_ffmpeg, capture_output=True)
@@ -276,7 +280,7 @@ class FileService:
'-H', f'Authorization: Bearer {token}',
'-H', 'Content-Type: audio/x-pcm;bit=16;rate=16000',
'-H', f'RqUID: {str(uuid.uuid4())}',
'--data-binary', f'@{pcm_output}'
'--data-binary', f'@{safe_pcm_output}'
]
return subprocess.run(curl_cmd, capture_output=True, text=True)
@@ -326,7 +330,6 @@ class FileService:
try:
def _sync_request():
return requests.post(url, headers=headers, data=payload, verify=False)
# ОБЁРТКА: синхронный запрос выполняется в отдельном потоке
response = await asyncio.to_thread(_sync_request)
token = response.json().get('access_token')
logger.info("SaluteSpeech token получен")
@@ -479,7 +482,7 @@ class FileService:
return text, count
# ============================================================
# ОСНОВНОЙ МЕТОД теперь асинхронный!
# ОСНОВНОЙ МЕТОД
# ============================================================
async def process_any_file(self, file_path: str) -> Tuple[str, int]: