videoDownloadTGbot/ARCHITECTURE.md
vrubelroman d597a5e1c5
All checks were successful
CI/CD Pipeline / build-and-deploy (push) Successful in 2m36s
fix(downloaders): Deno runtime, свежий yt-dlp, честный health-check, смоук-тест
Возрастные YouTube-видео не скачивались: с cookies yt-dlp отбрасывает клиент
android и остаётся web, которому нужно решить n-challenge, а в образе стоял
Node 20 при требуемом минимуме 22 ("JS runtimes: node-20.19.2 (unsupported)").
Ставим Deno — рекомендованный yt-dlp рантайм и один статический бинарник.

Instagram падал с "empty media response" одинаково с cookies и без них —
дело было не в сессии, а в устаревшем экстракторе: слой pip был закеширован
на yt-dlp 2026.06.09. Поднимаем нижнюю границу до 2026.7.4.

/cookies/check помечал проблемой с cookies ЛЮБОЙ сбой, из-за чего на поломку
JS-рантайма прилетел алерт про протухшие cookies и увёл разбор не туда.
Теперь ответ содержит status: ok | cookies_invalid | extraction_failed |
no_cookies, и админ-бот шлёт разные сообщения. Разбор ответа в bot.py
сохраняет совместимость со старым форматом без поля status.

Добавлен smoke_test.py — гоняет реальные ссылки (включая обе регрессии выше)
через запущенные сервисы и печатает таблицу. Запускать после каждой правки.

Схема получения cookies переведена с крона на разовый ручной экспорт:
cookies-cron/ -> cookies/, удалены скрипты с анти-паттерном
`--cookies-from-browser BROWSER --cookies FILE`, который wiki yt-dlp прямо
запрещает и который сам ломал YouTube-сессию ротацией. Ключевые cookies живут
около года, поэтому обновление по расписанию не нужно — триггером служит алерт
health-check. deliver_cookies.sh только доставляет файлы по scp.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 00:31:16 +00:00

254 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Архитектура системы
Документ описывает внутреннюю архитектуру и принципы работы Telegram Video Download Bot.
## Общая архитектура
Система состоит из микросервисной архитектуры с раздельными сервисами для каждого источника видео:
1. **Основной бот** (`bot.py`) — Telegram бот, обрабатывающий запросы пользователей
2. **YouTube Downloader Service** (`youtube-downloader/`) — порт 5557
3. **Instagram Downloader Service** (`instagram-downloader/`) — порт 5556
4. **VK Downloader Service** (`vk-downloader/`) — порт 5555
5. **Yapfiles Downloader Service** (`yapfiles-downloader/`) — порт 5558
6. **TikTok Downloader Service** (`tiktok-downloader/`) — порт 5559
```
┌─────────────────┐
│ Telegram User │
└────────┬────────┘
┌─────────────────────────────────────────────────────────┐
│ Основной бот (bot.py) │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Message Handler │ │
│ │ - URL extraction │ │
│ │ - Source detection (YouTube/Instagram/TikTok/VK/ │ │
│ │ Yapfiles) │ │
│ │ - Localization (ru/en) │ │
│ └────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ HTTP API Clients (httpx) │ │
│ │ - YouTube, Instagram, TikTok, VK, Yapfiles │ │
│ └────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ SQLite Database (data/bot.db) │ │
│ │ - users (chat_id, username, locale, ...) │ │
│ │ - stats (total_downloads) │ │
│ └────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
│ HTTP POST /download/stream
┌────────┬────────┬────────┬────────┬────────┐
│YouTube │Instagram│ TikTok │ VK │Yapfiles│
│ :5557 │ :5556 │ :5559 │ :5555 │ :5558 │
│ │ │ │ │ │
│yt-dlp │yt-dlp │yt-dlp │yt-dlp │requests│
│ │+cookies│ │ │+parsing│
└────────┴────────┴────────┴────────┴────────┘
```
## Компоненты системы
### 1. Основной бот (bot.py)
**Технологии:**
- `python-telegram-bot` — асинхронный фреймворк для Telegram Bot API
- `httpx` — асинхронный HTTP клиент
- `sqlite3` — база данных
**Ключевые функции:**
#### Локализация
```python
TEXTS = {
'ru': { 'start': '...', 'support': '...', ... },
'en': { 'start': '...', 'support': '...', ... }
}
def get_locale_from_language_code(language_code: str) -> str:
# 'ru*' → 'ru', иначе → 'en'
```
#### Определение источника
```python
def detect_video_source(url: str) -> str:
# youtube.com, youtu.be → 'youtube'
# instagram.com → 'instagram'
# tiktok.com → 'tiktok'
# vk.com, vkontakte.ru → 'vk'
# yapfiles.ru → 'yapfiles'
# иначе → 'unknown'
```
#### Команды
- `/start` — приветствие с локализацией
- `/stat` — статистика бота
- `/support` — информация о боте и контакт автора
### 2. Сервисы загрузчиков
Все сервисы имеют единый API:
| Endpoint | Метод | Описание |
|----------|-------|----------|
| `/health` | GET | Проверка работоспособности |
| `/download/stream` | POST | Скачивание видео |
**Request:**
```json
{"url": "https://..."}
```
**Response:** Бинарные данные видео или JSON с ошибкой.
#### YouTube Downloader (порт 5557)
- Использует `yt-dlp` с настройками для YouTube
- Поддержка shorts, плейлистов
#### Instagram Downloader (порт 5556)
- Использует `yt-dlp` с cookies
- Требует `instagram_cookies.txt`
#### TikTok Downloader (порт 5559)
- Использует `yt-dlp`
- Поддержка коротких и полных ссылок
#### VK Downloader (порт 5555)
- Использует `yt-dlp` с русскими заголовками
- Не требует VPN в РФ
#### Yapfiles Downloader (порт 5558)
- Парсит HTML страницу
- Извлекает прямую ссылку на видео
- Использует `requests` + `BeautifulSoup`
### 3. База данных
**Схема:**
```sql
CREATE TABLE users (
chat_id INTEGER PRIMARY KEY,
username TEXT,
first_name TEXT,
first_seen TEXT NOT NULL,
last_seen TEXT NOT NULL,
locale TEXT DEFAULT 'en' -- ru/en
);
CREATE TABLE stats (
id INTEGER PRIMARY KEY CHECK (id = 1),
total_downloads INTEGER DEFAULT 0
);
```
**Миграция:** При запуске бот автоматически добавляет колонку `locale` если её нет (для совместимости со старой базой).
### 4. Скрипт рассылки (broadcast.py)
Консольная утилита для отправки сообщений всем пользователям:
```bash
./broadcast.py -y "Текст сообщения"
./broadcast.py -y --html "<b>Жирный</b> текст"
./broadcast.py -y --file message.txt
./broadcast.py --list # показать пользователей
```
Автоматически читает `.env` для получения токена бота.
## Docker архитектура
### Порты
| Сервис | Внешний порт | Внутренний порт |
|--------|--------------|-----------------|
| VK | 5555 | 5000 |
| Instagram | 5556 | 5000 |
| YouTube | 5557 | 5000 |
| Yapfiles | 5558 | 5000 |
| TikTok | 5559 | 5000 |
### Volumes
**Основной бот:**
- `./video:/app/video` — скачанные видео
- `./data:/app/data` — база данных
**Сервисы загрузчиков:**
- `./downloads:/app/downloads` — временные файлы
- (YouTube) `./youtube_cookies.txt:/app/youtube_cookies.txt`
- (Instagram) `./instagram_cookies.txt:/app/instagram_cookies.txt`
Cookies монтируются с хоста (не read-only — файл обновляется вручную по `scp` с машины, где есть браузер), а не запекаются в образ — это позволяет обновлять их без пересборки/передеплоя сервиса. Процедура — в `cookies/README.md`.
## Потоки данных
```
User → Telegram → Bot
detect_video_source(url)
download_{source}_video()
HTTP POST → Service → yt-dlp/parser
binary video data
save to video/ → send to User
increment_downloads()
```
## Развертывание
### Текущая схема: единый docker-compose + CI/CD
Все 7 сервисов собираются и запускаются одним `docker-compose.yml` из корня репозитория (build-контексты на подпапки, единый `docker compose up -d --build`). Прод деплоится автоматически Forgejo Actions (`.forgejo/workflows/deploy.yml`) при пуше в `main`:
```
push в main → сборка всех 7 образов → push в Gitea registry
→ docker save | ssh | docker load на прод-хост
(registry недоступен напрямую с внешнего VPS)
→ docker compose up -d --remove-orphans на проде
```
На проде используется `docker-compose.prod.yml` (только `image:`, без `build:`).
### Гипотетический вариант: раздельное развёртывание
**Хост 1 (с VPN для YouTube/Instagram/TikTok):**
- Основной бот
- YouTube, Instagram, TikTok сервисы
**Хост 2 (без VPN):**
- VK, Yapfiles сервисы
В `.env` на хосте 1:
```env
VK_DOWNLOADER_URL=http://<host2_ip>:5555
YAPFILES_DOWNLOADER_URL=http://<host2_ip>:5558
```
## Безопасность
- Токен бота в `.env` (не коммитится)
- Cookies YouTube/Instagram закоммичены в репозиторий как fallback внутри образа; актуальные версии кладутся на хост вручную и монтируются поверх (см. `README.md#cookies-на-проде` и `cookies/README.md`)
- Сервисы доступны только по указанным URL
- Можно добавить API key между сервисами
## Мониторинг
- Все сервисы логируют в stdout
- Health check: `GET /health` на каждом сервисе
- Логи Docker: `docker compose logs -f`
## Будущие улучшения
1. **Аутентификация между сервисами** — API key/JWT
2. **Очередь задач** — Celery для фоновой обработки
3. **Prometheus метрики** — мониторинг производительности
4. **Кеширование** — Redis для частых запросов
5. **Поддержка новых источников** — Twitter, Facebook, etc.