diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..652584a --- /dev/null +++ b/.dockerignore @@ -0,0 +1,60 @@ +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +env/ +venv/ +ENV/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# Git +.git/ +.gitignore + +# Данные пользователя +uploads/ +processed/ +tmp/ +output/ +video/ +*.mp4 +*.wav +*.docx +*.md +*.txt + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db + +# Логи +server.log +*.log + +# Секреты +.env +.env.local +*.key +*.secret diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c9da877 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,36 @@ +# syntax=docker/dockerfile:1 + +FROM python:3.11-slim-bookworm + +# Установка системных зависимостей +RUN apt-get update && apt-get install -y --no-install-recommends \ + ffmpeg \ + build-essential \ + libsndfile1 \ + && rm -rf /var/lib/apt/lists/* + +# Рабочая директория +WORKDIR /app + +# Копируем зависимости +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Копируем код проекта +COPY . . + +# Предзагрузка моделей (без HF_TOKEN диаризация пропускается) +RUN python scripts/download_models.py || echo "Модели будут загружены при первом запуске" + +# Создаём директории для данных +RUN mkdir -p uploads processed tmp + +# Открываем порт +EXPOSE 8000 + +# Entrypoint скрипт +COPY scripts/docker-entrypoint.sh /docker-entrypoint.sh +RUN chmod +x /docker-entrypoint.sh + +ENTRYPOINT ["/docker-entrypoint.sh"] +CMD ["uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..7413d22 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,29 @@ +version: "3.8" + +services: + transcription: + build: . + container_name: transcription_service + ports: + - "8000:8000" + environment: + - HF_TOKEN=${HF_TOKEN} + - PYTHONUNBUFFERED=1 + volumes: + - uploads:/app/uploads + - processed:/app/processed + - tmp:/app/tmp + # Модели кэшируются в контейнере, но можно пробросить для переиспользования: + # - model_cache:/root/.cache + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/api/files"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + +volumes: + uploads: + processed: + tmp: diff --git a/scripts/docker-entrypoint.sh b/scripts/docker-entrypoint.sh new file mode 100644 index 0000000..2576994 --- /dev/null +++ b/scripts/docker-entrypoint.sh @@ -0,0 +1,22 @@ +#!/bin/bash +set -e + +echo "🚀 Transcription Service Docker Container" +echo "==========================================" + +# Проверяем HF_TOKEN +if [ -z "$HF_TOKEN" ]; then + echo "⚠️ Внимание: HF_TOKEN не установлен. Диаризация будет недоступна." + echo " Запустите с: docker compose up -e HF_TOKEN=your_token" +else + echo "✅ HF_TOKEN установлен" +fi + +# Создаём директории если их нет +mkdir -p uploads processed tmp + +echo "📡 Запуск сервера на http://0.0.0.0:8000" +echo "" + +# Запускаем uvicorn (worker'ы стартуют через lifespan в backend/main.py) +exec "$@" diff --git a/scripts/download_models.py b/scripts/download_models.py new file mode 100644 index 0000000..eb71de5 --- /dev/null +++ b/scripts/download_models.py @@ -0,0 +1,71 @@ +"""Скрипт предзагрузки моделей для Docker образа. + +Запускается во время сборки Docker образа. +Загружает все необходимые модели, чтобы при старте контейнера +не нужно было ждать скачивания. +""" + +import os +import sys + +# Устанавливаем токен HuggingFace из env или placeholder +os.environ.setdefault("HF_TOKEN", os.environ.get("HF_TOKEN", "")) + +import whisperx + + +def download_whisper_model(model_name="large-v3"): + """Загружает модель Whisper.""" + print(f"[Download] Whisper модель: {model_name}") + device = "cpu" + compute_type = "int8" + model = whisperx.load_model(model_name, device, compute_type=compute_type) + print(f"[Download] Whisper {model_name} готова") + del model + + +def download_alignment_model(language="ru"): + """Загружает alignment модель для языка.""" + print(f"[Download] Alignment модель для {language}") + device = "cpu" + model_a, metadata = whisperx.load_align_model(language_code=language, device=device) + print(f"[Download] Alignment {language} готова") + del model_a + + +def download_diarization_model(): + """Загружает модель диаризации.""" + print("[Download] Модель диаризации") + token = os.environ.get("HF_TOKEN", "") + if not token: + print("[Warning] HF_TOKEN не установлен — модель диаризации не загружена!") + print("[Warning] При старте контейнера установите HF_TOKEN через env.") + return + from whisperx.diarize import DiarizationPipeline + device = "cpu" + diarize_model = DiarizationPipeline(token=token, device=device) + print("[Download] Диаризация готова") + del diarize_model + + +def main(): + print("=" * 60) + print("Предзагрузка моделей для Docker образа") + print("=" * 60) + + download_whisper_model("large-v3") + download_alignment_model("ru") + + # Диаризация требует токен, поэтому опционально + try: + download_diarization_model() + except Exception as e: + print(f"[Warning] Диаризация не загружена: {e}") + + print("=" * 60) + print("Предзагрузка завершена") + print("=" * 60) + + +if __name__ == "__main__": + main()