Повністю автоматизований AI-конвеєр для генерації YouTube Shorts (та TikTok/Reels). На вхід — лише тема каналу, на виході — готове вертикальне відео з озвучкою, субтитрами, SEO-метаданими та опціональним завантаженням на YouTube.
| Компонент | Технологія |
|---|---|
| API | FastAPI + Uvicorn |
| Task Queue | Celery + Redis |
| DB | SQLite (2 бази: shorts.db + memory.db) |
| ORM | SQLAlchemy 2.0 + Alembic |
| LLM | g4f (безкоштовно) → Ollama → OpenAI (фолбек-ланцюг) |
| TTS | edge-tts (Microsoft, безкоштовно) |
| Відео | MoviePy 1.0.3 + FFmpeg |
| Візуали | Pexels/Pixabay (стоки) + Pollinations (AI) |
| Тренди | pytrends + PRAW (Reddit) |
| UI | Streamlit |
| Deploy | Docker Compose (6 сервісів) |
| Мова | Python 3.11 |
make setup # Перший запуск: build, start, init DB
make up # Старт сервісів
make run-now # Тригер генерації через API
make logs-worker # Перегляд логів worker
/api/outputs/{filename}Файл: app/main.py
Проблема: Ендпоінт serve_output не перевіряв, чи filename містить ../. Атакуючий міг отримати доступ до будь-якого файлу на сервері через GET /api/outputs/../../../../etc/passwd.
Виправлення: Додано resolve() + перевірку, що шлях залишається всередині OUTPUTS_DIR.
Файл: app/providers/visuals/pexels_provider.py
Проблема: f.get("width", 9999) <= f.get("height", 1) — дефолтні значення інвертовані. Якщо в API-відповіді відсутні width/height, відео ніколи не вважалося portrait.
Виправлення: Змінено на f.get("width", 0) <= f.get("height", 9999).
Файл: app/providers/tts/edge_tts_provider.py
Проблема: asyncio.run() падає з RuntimeError, якщо вже є активний event loop (можливо в Celery worker).
Виправлення: Перевірка наявності running loop + виконання через ThreadPoolExecutor як запасний варіант.
Файл: app/pipeline/step_05_edit.py
Проблема: Якщо відео-кліп дуже короткий (0.1с), reps = int(duration / clip.duration) + 1 може дати тисячі повторень → OOM.
Виправлення: Додано ліміт min(..., 10) на кількість повторень.
Файл: app/pipeline/step_05_edit.py
Проблема: Після write_videofile() нікуди не перевірялось, чи файл реально створено і не порожній.
Виправлення: Додано перевірку exists() + мінімальний розмір файлу.
Файл: app/api/routes_config.py
Проблема: open + write — якщо процес помер під час запису, config.yaml залишиться зіпсованим.
Виправлення: Запис через тимчасовий файл + os.replace() (атомарна операція на рівні FS).
| # | Проблема | Файл | Виправлення |
|---|---|---|---|
| 7 | Відсутні DB-індекси на status, created_at, topic |
models.py |
Додано Index() |
| 8 | Відсутній індекс на ChannelMemory.topic |
memory_models.py |
Додано Index() |
| 9 | Немає валідації кількості сегментів у скрипті | step_02_script.py |
Перевірка 1-10 сегментів |
| 10 | Ollama response["message"]["content"] — KeyError |
ollama_provider.py |
.get() з перевіркою |
| 11 | OpenAI — .content може бути None |
openai_provider.py |
Перевірка на None |
| 12 | DELETE memory видаляв ВСІ MemoryEvents, не тільки для топіку | routes_memory.py |
Фільтр по topic |
| 13 | Beat schedule мовчки ковтав помилки | beat_schedule.py |
Логування помилок |
| 14 | FFmpeg crop filter — непрозорі рядкові вирази | ffmpeg_utils.py |
Виділення в змінні |
| 15 | Upload hardcoded en-US audio language |
step_07_upload.py |
Виправлено на en |
| # | Проблема | Складність |
|---|---|---|
| 1 | piper_provider.py не існує, але є у factory |
Низька — додати заглушку або прибрати з enum |
| 2 | comfyui_provider.py не існує, але є у factory |
Низька — аналогічно |
| 3 | g4f_provider має 4 рівні фолбеку, складно дебажити | Середня — рефактор на retry pattern |
| 4 | _extract_json() дубльована у 3 файлах |
Низька — винести у utils |
| 5 | CORS allow_origins=["*"] |
Середня — обмежити для prod |
| 6 | Логи тільки в stdout, не персистяться | Середня — додати file handler |
| 7 | asset_cache_path() використовує MD5 |
Низька — міграція на SHA-256 |
| 8 | Random seed в Pollinations (seed=42) — немає різноманітності | Низька — зробити random |
| 9 | config reload не тригерить перезапуск workers | Висока — потрібен config watcher |
┌──────────────┐ ┌──────────┐ ┌────────────────┐
│ Streamlit │───▶│ FastAPI │◀──▶│ SQLite DBs │
│ UI :8501 │ │ :8000 │ │ shorts.db │
└──────────────┘ └────┬─────┘ │ memory.db │
│ └────────────────┘
│ trigger
▼
┌──────────┐ ┌────────────────┐
│ Redis │◀──▶│ Celery Worker │
│ :6379 │ │ (concurrency=2) │
└──────────┘ └────────┬───────┘
▲ │
┌────┴─────┐ │ runs pipeline
│ Celery │ ▼
│ Beat │ ┌────────────────────┐
└──────────┘ │ PipelineOrchestrator│
└────────┬───────────┘
│
┌───────────┬────────────┼─────────────┬──────────┐
▼ ▼ ▼ ▼ ▼
Step 01 Step 02 Step 03-05 Step 06-07 Step 08-09
Trends Script Visuals/ SEO/Upload Notify/
(pytrends (LLM) Audio/Edit (LLM/ Reflect
+ Reddit) (TTS+MoviePy) YouTube) (Telegram)
Кожен крок пайплайну отримує та мутує спільний ctx: dict. Кроки loosely coupled — крок N читає ключі, записані кроками 1..N-1.
ctx = {"job_id": "...", "topic": "AI", "config": AppConfig(...)}
# Step 01 додає: ctx["trend_keyword"] = "GPT-5 release"
# Step 02 додає: ctx["script"] = {...}, ctx["script_full_text"] = "..."
# Step 03 додає: ctx["visual_assets"] = [...]
# ...
Переваги: Простота, гнучкість, легко додавати нові кроки.
Недоліки: Немає type-safety, складно відстежити хто що пише/читає.
LLM, TTS, візуали, тренди — все реалізовано через абстрактні базові класи + фабрики:
LLMProvider (ABC) VisualProvider (ABC)
├── G4FProvider ├── PexelsProvider
├── OllamaProvider ├── PixabayProvider
└── OpenAIProvider └── PollinationsProvider
Фабрики (build_llm_provider, build_stock_provider) зчитують конфіг і повертають відповідну імплементацію. Є fallback chain: g4f → Ollama → OpenAI.
Кожен крок — клас, що наслідує PipelineStep:
- name — ідентифікатор
- required — чи зупиняти пайплайн при помилці
- is_disabled() — перевірка конфігу
- run(ctx, cfg) — основна логіка
PipelineOrchestrator виконує кроки послідовно, пишучи статус в DB.
shorts.db — основна БД (Job, JobStep, SEOPackage, Upload, Analytics). Управляється Alembic.memory.db — БД пам'яті каналу (ChannelMemory, MemoryEvent). Окремий engine.Розділення дозволяє легко чистити memory без впливу на історію робіт.
1. TRENDS → trend_keyword ("GPT-5 release date")
2. SCRIPT → script JSON (hook + 4 segments + CTA), ~55s
3. VISUALS → 4 video/image assets (Pexels + Pollinations)
4. AUDIO → voiceover.mp3 + subtitles.vtt + background_music.mp3
5. EDIT → final_video.mp4 (1080x1920, 30fps, karaoke subs)
6. SEO → title, description, tags, hashtags
7. UPLOAD → YouTube video_id + URL
8. NOTIFY → Telegram message with preview
9. REFLECT → ChannelMemory update (insights for next videos)
| Сервіс | Роль | Порт |
|---|---|---|
redis |
Celery broker + result backend | 6379 |
app |
FastAPI REST API | 8000 |
worker |
Celery worker (2 threads) | — |
beat |
Celery Beat (cron scheduler) | — |
ui |
Streamlit dashboard | 8501 |
ollama |
Локальний LLM (опціонально) | 11434 |
comfyui |
Stable Diffusion (profile, опціонально) | 8188 |
_extract_json(), _proportional_durations(), _parse_vtt()PipelineOrchestrator з мок-крокамиAPI тести для ендпоінтів
Лінтинг та CI — Додати ruff + mypy + GitHub Actions pipeline
Dry-run mode — test_pipeline.py приймає --dry-run, але не мокає провайдери. Реалізувати мок-провайдери для всіх сервісів
Health check у Dockerfile — Додати HEALTHCHECK CMD curl --fail http://localhost:8000/health
Вінести _extract_json() — Єдина утиліта замість 3 копій
Мультимовність — Наразі hardcoded "en-US" в upload, SEO, script prompts. Зробити channel.language домінуючим параметром для всіх LLM-промптів та TTS голосу
Retry & Circuit Breaker — Замінити bare try/except у провайдерах на tenacity декоратори з exponential backoff та circuit breaker для зовнішніх API
Prometheus метрики — Додати /metrics ендпоінт:
pipeline_runs_total{status}pipeline_step_duration_seconds{step}api_requests_total{endpoint}celery_tasks_active
Persistent logging — Логи зникають при рестарті контейнера. Додати:
Або JSON logs → Loki/ELK стек
TikTok upload — UploadTikTokConfig існує в схемі, але не реалізований. Інтеграція через TikTok for Developers API
A/B тестування — Генерувати 2-3 варіанти title/thumbnail, автоматично вибирати найкращий за CTR через Analytics API
Config hot-reload — При оновленні config через API, відправляти сигнал workers для перезавантаження конфігу без перезапуску
AI Voice Cloning — Інтеграція із ElevenLabs або XTTS для унікального голосу каналу замість стандартних Edge TTS голосів
Автоматичний B-Roll — AI-аналіз скрипту для підбору тематично точніших візуалів замість простого keyword-пошуку
YouTube Analytics feedback loop — Автоматичне збирання views/retention через YouTube Analytics API → навчання моделі на реальних метриках перформансу
Multi-channel — Підтримка декількох каналів з різними темами, мовами та стилями в одному інстансі
PostgreSQL міграція — SQLite не масштабується для concurrent writes. PostgreSQL або MySQL для production
Kubernetes deploy — Helm chart для горизонтального скейлінгу workers
Plugin system — Дати можливість додавати кастомні кроки пайплайну, провайдери, пост-процесори через plugin API
Генерація Shorts із існуючого контенту — Автоматичне вирізання найцікавіших моментів із довгих відео (YouTube/podcast) у shorts-формат
Real-time preview — WebSocket-стрім прогресу генерації з preview кожного кроку в UI
GPU acceleration — CUDA-прискорення для FFmpeg та Stable Diffusion, автоматичне визначення доступного GPU
Typed Context — Замінити dict[str, Any] на Pydantic-модель PipelineContext з чіткими полями для кожного кроку
Event Sourcing для Job — Замість мутації статусу, зберігати лог подій (started, step_completed, failed, etc.)
Async Pipeline — Деякі кроки (визуали, аудіо) можна виконувати паралельно через asyncio.gather() або Celery chord
Rate Limiter — Централізований rate limiter для всіх зовнішніх API (Pexels, Pixabay, Google Trends) замість розрізнених try/except
Проект має солідну архітектуру з правильними патернами (provider pattern, pipeline steps, context dict). Основна кодова база добре структурована та легко розширюється.
Головні ризики:
- Відсутність тестів (один баг у одному кроці ламає весь пайплайн)
- Залежність від зовнішніх безкоштовних сервісів (g4f, edge-tts, Pollinations)
- SQLite не витримає concurrent load при масштабуванні
Головні сильні сторони:
- 100% Docker, zero manual setup (крім YouTube OAuth)
- Повний цикл від ідеї до публікації
- Graceful degradation — опціональні кроки не зупиняють пайплайн
- Memory loop — канал "навчається" з кожним відео