Рефакторинг: микросервисная архитектура

- Разделение на микросервисы: youtube-downloader, instagram-downloader, vk-downloader
- Основной бот в корне проекта, работает через HTTP API с сервисами
- Каждый сервис запускается отдельно в своей папке
- Видео сохраняются в папке video/ и не удаляются
- Обновлена документация и архитектура
- Скрипты для Instagram cookies перенесены в instagram-downloader/
This commit is contained in:
vrubelroman 2025-12-11 01:07:04 +03:00
parent 8024eea868
commit 436e0cd541
41 changed files with 1348 additions and 693 deletions

View file

@ -4,10 +4,12 @@
## Общая архитектура
Система состоит из двух основных компонентов:
Система состоит из микросервисной архитектуры с раздельными сервисами для каждого источника видео:
1. **Основной бот** (`bot.py`) — Telegram бот, обрабатывающий запросы пользователей
2. **VK Downloader Service** (`vk-downloader/`) — отдельный микросервис для скачивания видео с VK
1. **Основной бот** (`bot.py` в корне проекта) — Telegram бот, обрабатывающий запросы пользователей и оркестрирующий запросы к сервисам
2. **YouTube Downloader Service** (`youtube-downloader/`) — микросервис для скачивания видео с YouTube
3. **Instagram Downloader Service** (`instagram-downloader/`) — микросервис для скачивания видео с Instagram
4. **VK Downloader Service** (`vk-downloader/`) — микросервис для скачивания видео с VK
```
┌─────────────────┐
@ -18,44 +20,55 @@
┌─────────────────────────────────────┐
│ Основной бот (bot.py) │
│ ┌───────────────────────────────┐ │
│ │ YouTube Download Handler │ │
│ │ Message Handler │ │
│ │ - URL extraction │ │
│ │ - Source detection │ │
│ └───────────────────────────────┘ │
│ ┌───────────────────────────────┐ │
│ │ Instagram Download Handler │ │
└───────────────────────────────┘
┌───────────────────────────────┐
│ │ VK API Client │──┼──┐
│ │ HTTP API Clients │──┼──┐
│ - YouTube API Client │ │
│ - Instagram API Client │ │
│ │ - VK API Client │ │ │
│ └───────────────────────────────┘ │ │
│ ┌───────────────────────────────┐ │ │
│ │ SQLite Database │ │ │
│ │ - Users │ │ │
│ │ - Stats │ │ │
│ └───────────────────────────────┘ │ │
│ ┌───────────────────────────────┐ │ │
│ │ Video Storage │ │ │
│ │ - video/ directory │ │ │
│ └───────────────────────────────┘ │ │
└─────────────────────────────────────┘ │
│ HTTP API
│ POST /download/stream
┌──────────────────────┐
│ VK Downloader │
│ Service │
│ ┌────────────────┐ │
│ │ Flask API │ │
│ └────────────────┘ │
│ ┌────────────────┐ │
│ │ yt-dlp │ │
│ │ (VK only) │ │
│ └────────────────┘ │
└──────────────────────┘
│ HTTP API
│ POST /download/stream
┌──────────────────────────────────┼──────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ YouTube Downloader│ │Instagram Download│ │ VK Downloader │
│ Service │ │ Service │ │ Service │
│ ┌──────────────┐ │ │ ┌──────────────┐ │ │ ┌──────────────┐│
│ │ Flask API │ │ │ │ Flask API │ │ │ │ Flask API ││
│ └──────────────┘ │ │ └──────────────┘ │ │ └──────────────┘│
│ ┌──────────────┐ │ │ ┌──────────────┐ │ │ ┌──────────────┐│
│ │ yt-dlp │ │ │ │ yt-dlp │ │ │ │ yt-dlp ││
│ │ (YouTube) │ │ │ │ (Instagram) │ │ │ │ (VK only) ││
│ └──────────────┘ │ │ └──────────────┘ │ │ └──────────────┘│
│ Port: 5557 │ │ Port: 5556 │ │ Port: 5555 │
└──────────────────┘ └──────────────────┘ └──────────────────┘
```
## Компоненты системы
### 1. Основной бот (bot.py)
**Расположение:** Корень проекта
**Технологии:**
- `python-telegram-bot` (v20.7) — асинхронный фреймворк для Telegram Bot API
- `yt-dlp` — библиотека для скачивания видео
- `httpx` — асинхронный HTTP клиент для запросов к VK сервису
- `httpx` — асинхронный HTTP клиент для запросов к сервисам загрузчиков
- `sqlite3` — база данных для хранения статистики
**Архитектурные решения:**
@ -64,10 +77,20 @@
- Асинхронная обработка через `asyncio`
- Каждый пользовательский запрос обрабатывается независимой корутиной
- Параллельная обработка нескольких запросов
- Автоматическое извлечение URL из текста сообщений (работа в группах)
#### Скачивание видео
- **YouTube/Instagram**: Прямое скачивание через `yt-dlp` в executor (не блокирует event loop)
- **VK**: HTTP запрос к внешнему микросервису через `httpx`
- **Все источники**: HTTP запросы к соответствующим микросервисам через `httpx`
- **YouTube**: `POST http://youtube-downloader:5000/download/stream`
- **Instagram**: `POST http://instagram-downloader:5000/download/stream`
- **VK**: `POST http://vk-downloader:5000/download/stream`
- Получение бинарных данных и сохранение во временный файл
- Отправка через Telegram API
#### Хранение видео
- Все скачанные видео сохраняются в папке `video/` на хосте
- Файлы не удаляются автоматически (сохраняются для пользователей)
- Автоматическая очистка только `.part` файлов (недокачанные)
#### База данных
- SQLite с двумя таблицами:
@ -80,13 +103,67 @@
- Retry механизм для всех источников (3 попытки по умолчанию)
- Логирование всех ошибок
- Информативные сообщения пользователю
- Автоматическая очистка `.part` файлов при ошибках
### 2. VK Downloader Service
### 2. YouTube Downloader Service
**Расположение:** `youtube-downloader/`
**Технологии:**
- Flask — веб-фреймворк для REST API
- `yt-dlp` — библиотека для скачивания видео с YouTube
- Flask-CORS — для поддержки CORS
**API Endpoints:**
1. `GET /health` — проверка работоспособности сервиса
- Возвращает: `{"status": "ok", "service": "youtube-downloader"}`
2. `POST /download/stream` — скачивание видео
- Request: `{"url": "https://youtube.com/watch?v=..."}`
- Response: Бинарные данные видео (200 OK) или JSON с ошибкой (400/500)
**Особенности:**
- Использует специальные настройки yt-dlp для YouTube (player_client, headers)
- Поддержка различных форматов видео (mp4, webm, mkv)
- Временные файлы сохраняются в `downloads/` и удаляются после отправки
**Порт:** 5557 (внешний) → 5000 (внутренний)
### 3. Instagram Downloader Service
**Расположение:** `instagram-downloader/`
**Технологии:**
- Flask — веб-фреймворк для REST API
- `yt-dlp` — библиотека для скачивания видео с Instagram
- Flask-CORS — для поддержки CORS
**API Endpoints:**
1. `GET /health` — проверка работоспособности сервиса
- Возвращает: `{"status": "ok", "service": "instagram-downloader"}`
2. `POST /download/stream` — скачивание видео
- Request: `{"url": "https://instagram.com/p/..."}`
- Response: Бинарные данные видео (200 OK) или JSON с ошибкой (400/500)
**Особенности:**
- Требует файл с cookies Instagram (`instagram_cookies.txt` в папке сервиса)
- Автоматическая проверка срока действия cookies
- Поддержка CSRF токенов и session cookies
- Скрипты для получения/обновления cookies в папке сервиса
**Порт:** 5556 (внешний) → 5000 (внутренний)
### 4. VK Downloader Service
**Расположение:** `vk-downloader/`
**Технологии:**
- Flask — веб-фреймворк для REST API
- `yt-dlp` — библиотека для скачивания видео с VK
- Flask-CORS — для поддержки CORS (если нужно)
- Flask-CORS — для поддержки CORS
**API Endpoints:**
@ -97,22 +174,14 @@
- Request: `{"url": "https://vk.com/clip-..."}`
- Response: Бинарные данные видео (200 OK) или JSON с ошибкой (400/500)
**Особенности реализации:**
**Особенности:**
- Специальные заголовки для VK (User-Agent, Referer)
- Обработка кириллицы в именах файлов
- Временные файлы сохраняются в `downloads/` и удаляются после отправки
#### Обработка кириллицы
- Имена файлов с кириллицей конвертируются в ASCII для HTTP заголовков
- Оригинальное имя сохраняется в файловой системе
**Порт:** 5555 (внешний) → 5000 (внутренний)
#### Управление файлами
- Временные файлы сохраняются в `downloads/`
- Файлы удаляются после отправки клиенту
- Использование UUID для уникальности имен
#### Ограничения
- Flask dev server (однопоточный) — обрабатывает запросы последовательно
- Для продакшена рекомендуется использовать Gunicorn с несколькими worker'ами
### 3. Определение источника видео
### 5. Определение источника видео
Функция `detect_video_source(url)` анализирует домен URL:
@ -123,29 +192,56 @@
- иначе → 'unknown'
```
### 4. Потоки данных
### 6. Потоки данных
#### Скачивание с YouTube/Instagram
#### Общий поток скачивания
```
User → Bot → detect_video_source() → download_youtube_video() / download_instagram_video()
User → Bot → extract_urls_from_text() → detect_video_source()
yt-dlp (executor) → video file → Telegram API → User
```
#### Скачивание с VK
```
User → Bot → detect_video_source() → download_vk_video()
HTTP POST /download/stream → VK Service
HTTP POST /download/stream → [YouTube/Instagram/VK] Service
yt-dlp → video file → HTTP Response (binary)
Bot receives binary → saves to disk → Telegram API → User
Bot receives binary → saves to video/ → Telegram API → User
```
### 5. База данных
#### Детальный поток для каждого источника
**YouTube:**
```
User → Bot → download_youtube_video()
httpx.AsyncClient → POST http://youtube-downloader:5000/download/stream
YouTube Service → yt-dlp → binary data
Bot saves to video/ → sends to User
```
**Instagram:**
```
User → Bot → download_instagram_video()
httpx.AsyncClient → POST http://instagram-downloader:5000/download/stream
Instagram Service → yt-dlp (with cookies) → binary data
Bot saves to video/ → sends to User
```
**VK:**
```
User → Bot → download_vk_video()
httpx.AsyncClient → POST http://vk-downloader:5000/download/stream
VK Service → yt-dlp → binary data
Bot saves to video/ → sends to User
```
### 7. База данных
**Схема:**
@ -179,21 +275,72 @@ CREATE TABLE stats (
### Основной бот
**Расположение:** Корень проекта
**Образ:**
- Базовый: `python:3.11-slim`
- Зависимости: `ffmpeg`, `wget`
- Python пакеты из `requirements.txt`
- Python пакеты из `requirements.txt` (python-telegram-bot, httpx)
**Volumes:**
- `./video:/app/video` — временные файлы видео
- `./instagram_cookies.txt:/app/instagram_cookies.txt` — cookies для Instagram
- `./video:/app/video` — сохраненные видео (не удаляются)
- `./data:/app/data:Z` — база данных (SELinux relabel)
**Network:**
- `network_mode: host` — для доступа к VK сервису через localhost
- `network_mode: host` — для доступа к сервисам по localhost или IP
**Запуск:**
```bash
docker compose up -d
```
### YouTube Downloader Service
**Расположение:** `youtube-downloader/`
**Образ:**
- Базовый: `python:3.11-slim`
- Зависимости: `ffmpeg`, `wget`
- Python пакеты: Flask, flask-cors, yt-dlp
**Volumes:**
- `./downloads:/app/downloads` — временные файлы
**Ports:**
- `5557:5000` — внешний порт 5557, внутренний 5000
**Запуск:**
```bash
cd youtube-downloader && docker compose up -d
```
### Instagram Downloader Service
**Расположение:** `instagram-downloader/`
**Образ:**
- Базовый: `python:3.11-slim`
- Зависимости: `ffmpeg`, `wget`
- Python пакеты: Flask, flask-cors, yt-dlp
**Volumes:**
- `./downloads:/app/downloads` — временные файлы
- `./instagram_cookies.txt:/app/instagram_cookies.txt:ro` — cookies (read-only)
**Environment:**
- `INSTAGRAM_COOKIES_FILE=/app/instagram_cookies.txt`
**Ports:**
- `5556:5000` — внешний порт 5556, внутренний 5000
**Запуск:**
```bash
cd instagram-downloader && docker compose up -d
```
### VK Downloader Service
**Расположение:** `vk-downloader/`
**Образ:**
- Базовый: `python:3.11-slim`
- Зависимости: `ffmpeg`, `wget`
@ -205,22 +352,27 @@ CREATE TABLE stats (
**Ports:**
- `5555:5000` — внешний порт 5555, внутренний 5000
**Network:**
- Отдельная сеть `vk_network` (можно использовать host network для доступа)
**Запуск:**
```bash
cd vk-downloader && docker compose up -d
```
## Безопасность
### Переменные окружения
- Токен бота хранится в `.env` (не коммитится в Git)
- Cookies для Instagram хранятся в файле (не коммитится)
- Cookies для Instagram хранятся в файле `instagram-downloader/instagram_cookies.txt` (не коммитится)
- URL сервисов настраиваются через `.env`
### Ограничения доступа
- VK сервис доступен только по указанному IP/URL
- Нет аутентификации между ботом и VK сервисом (можно добавить API key)
- Сервисы загрузчиков доступны только по указанным IP/URL
- Нет аутентификации между ботом и сервисами (можно добавить API key)
- Cookies файл монтируется read-only
### Файловая система
- Временные файлы удаляются после отправки
- Cookies файл монтируется read-only (опционально)
- Видео сохраняются в `video/` и не удаляются автоматически
- Временные файлы в сервисах удаляются после отправки
- Автоматическая очистка только `.part` файлов
## Масштабирование
@ -228,15 +380,17 @@ CREATE TABLE stats (
1. **Основной бот:**
- Один экземпляр (можно запустить несколько с разными токенами)
- Параллельная обработка запросов ограничена ресурсами CPU/сети
- Параллельная обработка запросов через asyncio
- Ограничения: ресурсы CPU/сети
2. **VK сервис:**
2. **Сервисы загрузчиков:**
- Flask dev server — последовательная обработка
- Один экземпляр контейнера
- Один экземпляр контейнера каждого сервиса
- Можно масштабировать горизонтально
### Рекомендации для масштабирования
1. **VK сервис:**
1. **Сервисы загрузчиков:**
- Использовать Gunicorn с несколькими worker'ами:
```bash
gunicorn -w 4 -b 0.0.0.0:5000 app:app
@ -267,7 +421,7 @@ CREATE TABLE stats (
- Размер скачанных файлов
### Health checks
- VK сервис: `GET /health`
- Все сервисы: `GET /health`
- Основной бот: проверка через статус контейнера
## Производительность
@ -276,11 +430,11 @@ CREATE TABLE stats (
1. **Асинхронная обработка:**
- Основной бот использует asyncio для неблокирующих операций
- yt-dlp запускается в executor для YouTube/Instagram
- HTTP запросы к сервисам через httpx (асинхронный)
2. **Временные файлы:**
- Хранение в памяти (tmpfs) для быстрого доступа (опционально)
- Автоматическая очистка после отправки
2. **Хранение видео:**
- Видео сохраняются на хосте (volume), не в образе контейнера
- Файлы доступны для пользователей после скачивания
3. **Кеширование:**
- Можно добавить кеш информации о видео (title, duration)
@ -288,34 +442,44 @@ CREATE TABLE stats (
### Узкие места
1. **VK сервис:**
- Последовательная обработка запросов
1. **Сервисы загрузчиков:**
- Последовательная обработка запросов (Flask dev server)
- Скачивание файла перед отправкой (занимает память)
2. **Сеть:**
- Зависимость от скорости интернета
- Задержки при обращении к внешнему VK сервису
- Задержки при обращении к внешним сервисам
- Задержки между ботом и сервисами загрузчиков
## Развертывание
### Локальная разработка
```bash
# Основной бот
docker compose up -d
# VK сервис
cd vk-downloader && docker compose up -d
```bash
# Запуск сервисов загрузчиков (каждый в своей папке)
cd youtube-downloader && docker compose up -d
cd ../instagram-downloader && docker compose up -d
cd ../vk-downloader && docker compose up -d
# Запуск основного бота (из корня проекта)
cd ..
docker compose up -d
```
### Продакшен (раздельное развертывание)
**Хост 1 (с VPN):**
**Хост 1 (с VPN для YouTube/Instagram):**
- Основной бот
- YouTube сервис (порт 5557)
- Instagram сервис (порт 5556)
- VPN для доступа к YouTube/Instagram
- `.env`: `VK_DOWNLOADER_URL=http://<host2_ip>:5555`
- `.env`:
- `YOUTUBE_DOWNLOADER_URL=http://localhost:5557`
- `INSTAGRAM_DOWNLOADER_URL=http://localhost:5556`
- `VK_DOWNLOADER_URL=http://<host2_ip>:5555`
**Хост 2 (без VPN):**
- VK Downloader Service
**Хост 2 (без VPN для VK):**
- VK Downloader Service (порт 5555)
- Доступен по IP для хоста 1
- Можно масштабировать горизонтально
@ -328,7 +492,7 @@ cd vk-downloader && docker compose up -d
## Будущие улучшения
1. **Аутентификация между сервисами:**
- API key для VK сервиса
- API key для сервисов загрузчиков
- JWT токены
2. **Очередь задач:**
@ -339,8 +503,12 @@ cd vk-downloader && docker compose up -d
- Prometheus метрики
- Grafana дашборды
4. **Улучшение VK сервиса:**
4. **Улучшение сервисов:**
- Переход на FastAPI (более производительный)
- Поддержка WebSockets для прогресса
- Stream ответа вместо полной загрузки в память
5. **Хранение видео:**
- Организация по датам/пользователям
- Автоматическая очистка старых файлов (опционально)
- Интеграция с облачным хранилищем