# AE Anons - Автономный генератор спортивных анонсов в After Effects
[](https://opensource.org/licenses/MIT)
[](https://www.rust-lang.org/)
[](https://git.tvstart.ru/lexx/AE_Anons)
**AE Anons** — полностью автономная (standalone) система для создания спортивных анонсов с использованием шаблонов After Effects через Nexrender. Все данные берутся из электронных таблиц Synology Office, рендеринг выполняется локально, управление заданиями происходит через встроенный веб-интерфейс с мгновенными обновлениями. Система не требует доступа в интернет — все ресурсы (иконки, стили, шрифты) встроены в бинарный файл.
## Новое в версии v0.3.0
- **WebSocket** — мгновенные обновления списка заданий (без устаревшего polling каждые 60 секунд)
- **SQLite** — хранение истории заданий и статуса апрува
- **Просмотр видео** прямо в веб-интерфейсе (по ссылке на готовый файл)
- **Апрув заданий** с копированием видео на SMB-шару (без монтирования, с поддержкой Active Directory)
- **Уведомления в реальном времени** о старте/завершении генерации, ошибках
- **Улучшенный веб-интерфейс** с кнопками апрува и иконками превью
- **Оптимизация памяти** — строки таблицы обрабатываются потоково, неиспользуемые логотипы и ассеты удаляются после генерации. Пиковое потребление памяти достигается только в момент загрузки и парсинга Excel, после чего память освобождается. Это позволяет работать с таблицами большого объёма (десятки тысяч строк) и обеспечивает стабильную работу в режиме долгоживущего сервиса.
- Полная автономность — все статические ресурсы (HTML, CSS, Font Awesome, логотип, favicon) встроены в бинарный файл.
## Обзор
AE Anons — это CLI-утилита и веб-сервер на Rust, которая выступает как **интеллектуальный генератор заданий** для [Nexrender](https://github.com/inlife/nexrender) — опенсорсного оркестратора рендеринга After Effects.
### Режимы работы
1. **Однократная обработка** (`--once` или без флагов):
- Подключение к NAS Synology
- Парсинг Excel данных
- Генерация и отправка заданий Nexrender
- Мониторинг завершения и выход
2. **Веб-сервер** (`--web`):
- Запуск веб-интерфейса на порту `:3000` (настраивается)
- Управление заданиями через браузер с WebSocket-обновлениями
- Просмотр видео и апрув готовых роликов
## Особенности
- **Полная автономность** — не требует выхода в интернет, все ресурсы встроены в бинарник
- **Интеграция с Synology** — аутентификация и загрузка файлов с NAS Synology (локальные и AD-учётные записи)
- **Экспорт офисных таблиц** — автоматическое преобразование `.osheet` в Excel
- **Гибкий парсинг данных** — динамический парсинг листов с обнаружением заголовков
- **Множественная генерация вариантов** — "Сегодня", "Завтра" и датированные версии
- **Умное управление логотипами** — автоматическое разрешение и масштабирование
- **Веб-интерфейс с WebSocket** — мгновенные обновления, просмотр видео, апрув
- **REST API** — программное управление заданиями
- **SQLite** — хранение истории и статуса апрува
- **Поддержка SMB** — копирование утверждённых видео на сетевую шару (с AD-аутентификацией)
- **Профессиональное логирование** — структурированный журнал с настройкой уровня
- **Эффективная работа с памятью** — потоковая обработка строк, своевременное удаление неиспользуемых данных (логотипы, ассеты) позволяет обрабатывать таблицы с десятками тысяч строк без утечек памяти
## Предварительные требования
- Rust 1.70 или выше
- Доступ к NAS Synology с пакетами File Station и Office (локальный или доменный пользователь)
- Экземпляр _server_ и _worker_ (не менее одного) Nexrender
- Шаблоны After Effects (формат .aep или .aepx, совместимые с версией AE на worker)
- (Для апрува) Доступ к SMB-шаре с правами на запись
## Установка
git clone https://git.example.com/team/ae_anons.git
cd ae_anons
cargo build --release
cp .env.example .env
# отредактируйте .env согласно вашей инфраструктуре
## Конфигурация
Все настройки задаются через переменные окружения в файле `.env`.
### Основные переменные
| Переменная | Обязательна | Описание |
|-------------------------|-------------|---------------------------------------------------------------------------------|
| `NAS_FQDN` | Да | URL Synology NAS (*) |
| `NAS_USER` | Да | Имя пользователя (локального или доменного, например `DOMAIN\username`) |
| `NAS_PASS` | Да | Пароль |
| `NAS_FILE` | Да | Путь к `.osheet` файлу на NAS (например, `/Team Folder/schedule.osheet`) |
| `NEXRENDER_API_URL` | Да | API Nexrender (например, `http://nexrender:3050/api/v1/jobs`) |
| `OUTPUT_FOLDER` | Да | Папка, куда Nexrender сохраняет готовые видео (локальный путь) |
| `TEMPLATE_DOUBLE_SRC` | Да | Путь к AEP-шаблону для двух команд (например, `file:///templates/double.aepx`) |
| `TEMPLATE_SINGLE_SRC` | Да | Путь к AEP-шаблону для одной команды |
| `TEMPLATE_COMPOSITION` | Да | Имя композиции в проекте AE |
| `TEMPLATE_OUTPUT_MODULE`| Да | Имя модуля вывода в AE |
| `TEMPLATE_OUTPUT_EXT` | Да | Расширение выходного файла (например, `mp4`) |
| `WEB_PORT` | Нет | Порт веб-сервера (по умолчанию 3000) |
| `RUST_LOG` | Нет | Уровень логирования (info, debug, trace) |
| `SMB_UPLOAD_URL` | Да(**) | SMB-URL для апрува (например, `smb://storage/approved`) |
| `SMB_USERNAME` | Да(**) | Имя пользователя для SMB (локальный или `DOMAIN\user`) |
| `SMB_PASSWORD` | Да(**) | Пароль для SMB |
| `DATABASE_URL` | Нет | Путь к SQLite БД (по умолчанию `sqlite:ae_anons.db`) |
> (*) — для `NAS_FQDN` можно указывать протокол `http://` или `https://`. Безопаснее `https`, хотя для локальной сети допустим и `http`. Если NAS доступен по стандартному HTTPS-порту 443, протокол можно опустить (например, `nas.company.local`). Иначе указывайте полный URL с портом: `https://nas.company.local:5001` или `http://nas.local:5000`.
>
> (**) — обязательно, если используется функция апрува (веб-режим). В режиме `--once` апрув не требуется.
### Пример файла `.env`
# Synology NAS
NAS_FQDN="https://nas.company.local:5001"
NAS_USER="DOMAIN\\service_account"
NAS_PASS="strongpassword"
NAS_FILE="/Team Folder/Anonsy/sport.osheet"
# Nexrender
NEXRENDER_API_URL="http://render-01:3050/api/v1/jobs"
OUTPUT_FOLDER="/mnt/nexrender/output"
# After Effects Templates
TEMPLATE_DOUBLE_SRC="file:///mnt/templates/double_team.aepx"
TEMPLATE_SINGLE_SRC="file:///mnt/templates/single_team.aepx"
TEMPLATE_COMPOSITION="main"
TEMPLATE_OUTPUT_MODULE="h264"
TEMPLATE_OUTPUT_EXT="mp4"
# Web server
WEB_PORT="3000"
RUST_LOG="info"
# SMB для апрува (AD-совместимо)
SMB_UPLOAD_URL="smb://storage.company.local/approved_videos"
SMB_USERNAME="DOMAIN\\service_account"
SMB_PASSWORD="strongpassword"
# Database
DATABASE_URL="sqlite:/var/lib/ae_anons/ae_anons.db"
> **Примечание об аутентификации:**
> Synology API и SMB-библиотека `smb` поддерживают как локальных пользователей NAS, так и доменных (Active Directory). Для доменных пользователей используйте формат `DOMAIN\username` (обратный слеш необходимо экранировать в `.env` как `\\`). Проверено на Synology DSM 7.x и Samba AD.
## Использование
### Однократная обработка (cron или ручной запуск)
./target/release/ae_anons --once
### Веб-сервер (интерактивный режим)
./target/release/ae_anons --web
После запуска откройте браузер: `http://localhost:3000`
## Веб-интерфейс
- **Главная страница** — таблица всех заданий с полями: UID, имя файла, статус, превью (иконка видео), кнопка апрува, дата создания.
- **Обновления в реальном времени** через WebSocket — статусы заданий меняются мгновенно без перезагрузки страницы.
- **Кнопка «Generate»** — запускает парсинг таблицы Synology и создание новых заданий. Во время генерации кнопка блокируется, при завершении приходит уведомление.
- **Кнопка «Cleanup»** — удаляет из Nexrender задания со статусом finished/error.
- **Кнопка «Stop all»** — останавливает все активные (queued/started/processing) задания.
- **Просмотр видео** — клик по иконке видео открывает готовый файл в новой вкладке (поддерживается любой браузерный просмотр MP4).
- **Апрув** — клик по ✅ копирует видео на SMB-шару (путь из `SMB_UPLOAD_URL`) и помечает задание как approved. Повторный апрув невозможен.
### API эндпоинты
- `GET /api/jobs` — список всех заданий (из SQLite)
- `POST /api/generate` — запуск генерации
- `POST /api/cleanup` — очистка завершённых
- `POST /api/jobs/stop-all` — остановка активных
- `GET /api/status` — статус сервера
- `GET /api/video/:uid` — просмотр видеофайла
- `POST /api/approve/:uid` — апрув (копирование на SMB)
- `WS /ws` — WebSocket для получения событий (`JobUpdated`, `GenerationStarted`, `GenerationFinished`, `Error`)
## Уровни логирования
`RUST_LOG=debug ./ae_anons --web`
- **error** — только критические ошибки
- **warn** — предупреждения
- **info** — стандартная информация (по умолчанию)
- **debug** — детали API-вызовов, парсинга, SMB-операций
- **trace** — максимальная детализация
## Структура электронной таблицы
Файл Excel должен содержать следующие листы со специфичными структурами:
### Лист "SPORT"
Связывает названия видов спорта с соответствующими видео пакетами.
| SPORT | LINK |
|----------------|---------------------------------------------|
| Без оформления | \\server\share\path\to\null.mov |
| Футбол | \\server\share\path\to\football_pack.mov |
| Волейбол | \\server\share\path\to\volleyball_pack.mov |
### Лист "TEAMS"
Сопоставляет имена команд с их видами спорта и логотипами. Колонка SPORT заполняется из выпадающего списка (заполнена из листа SPORT).
Если несколько команд в одном виде спорта имеют одинаковые названия, но разные логотипы, используется разделитель хеш # для уникальной идентификации.
| TEAM | SPORT | LINK |
|-------------------|-----------|---------------------------------------------------|
| Галатасарай#Turki | Футбол | \\server\share\path\to\galatasaray_football.png |
| Галатасарай | Волейбол | \\server\share\path\to\galatasaray_volleyball.png |
| Галатасарай#UCL | Волейбол | \\server\share\path\to\galatasaray_ucl.png |
| Сомбатей##400 | Футбол | \\server\share\path\to\szombathely.png |
### Лист "CHANELL"
Сопоставляет названия каналов с их логотипами. Поля заполняются вручную без выпадающих списков.
| CHANELL | LINK |
|---------|-----------------------------------------|
| КАНАЛ | \\server\share\path\to\channel_logo.png |
| TRIUMPH | \\server\share\path\to\triumph_logo.png |
### Лист "Start"
Основной источник данных для генерации анонсов. Большинство полей заполняются из выпадающих списков, основанных на других листах.
Обязательные колонки: DATA, TIME, CHANELL, SPORT, LEAGUE, TEAM A, TEAM B
**Примечания:**
- Колонка LEAGUE обязательна для заполнения, но не имеет выпадающего списка. Проверка орфографии и опечаток отсутствует!
- Колонка DATA должна содержать дату в формате `ДД.ММ.ГГГГ` (например, `28.2.2026`).
| STATE | TRIPPLE | DATA | TIME | SPORT | LEAGUE | CHANELL | TEAM A | TEAM B |
|-------|---------|-----------|-------|---------|---------------------------|---------|-----------------|------------------|
| TRUE | TRUE | 27.2.2026 | 23:40 | Футбол | Чемпионат Португалии | START | Спортинг#ll#550 | Эшторил#ll#550 |
| FALSE | TRUE | 28.2.2026 | 12:55 | Волейбол| Чемпионат Турции. Женщины | TRIUMPH | Бешикташ | Галатасарай#Turki|
### Формат имен команд
Имена команд поддерживают два типа разделителей хэш #:
1. **Первый хеш**: Уникальный идентификатор для команд с одинаковыми названиями в одном виде спорта, или одинаковым названием в разных видах спорта.
2. **Второй хеш**: Целевой размер в пикселях для масштабирования логотипа
Формат: `DisplayName#UniqueID#TargetSize`
Примеры:
- `Галатасарай#Turki` - только уникальный идентификатор
- `Сомбатей##400` - только целевой размер (обратите внимание на двойной хеш)
- `Спортинг#ll#550` - оба идентификатор и целевой размер
## Выходные файлы
Рендеренные видео сохраняются в `OUTPUT_FOLDER` по шаблону:
`YYYYMMDD_Sport_League_TeamA_TeamB_Channel[_Variant].mp4`
При апруве файл копируется в SMB-шару с тем же именем.
## Рабочий процесс (веб-режим)
1. Пользователь заполняет таблицу Synology Office.
2. Нажимает «Generate» в веб-интерфейсе.
3. Сервер аутентифицируется на NAS, скачивает и парсит Excel.
4. Генерирует задания Nexrender, сохраняет их в SQLite и отправляет в Nexrender.
5. WebSocket уведомляет клиент о новых заданиях.
6. Nexrender рендерит видео, статус задания обновляется (периодический опрос или webhook).
7. Когда видео готово, пользователь видит иконку превью, может посмотреть видео и нажать апрув.
8. При апруве видео копируется на SMB-шару, задание помечается как approved.
## Требования к шаблонам After Effects
Не изменились — все слои `DATA`, `TIME_H`, `TIME_M`, `LEAGUE`, `SPORT`, `TEAMS`, `TEAM_A_LOGO`, `TEAM_B_LOGO`, `CHANELL`, `TOP` должны присутствовать согласно типу шаблона (DOUBLE/SINGLE). Поддержка масштабирования логотипов через выражения After Effects — осталась.
## Устранение неисправностей
### WebSocket не работает
- Проверьте, что браузер поддерживает WebSocket (все современные поддерживают).
- При использовании прокси (nginx) необходимо настроить Upgrade заголовки.
### Ошибка аутентификации на NAS для доменного пользователя
- Убедитесь, что в `.env` указано `DOMAIN\\username` (двойной обратный слеш).
- Проверьте, что NAS настроен на приём доменных учётных записей (DSM → Домен/LDAP).
### Ошибка SMB подключения
- Проверьте доступность шары: `smbclient -U DOMAIN/username -L //storage/`
- Убедитесь, что в URL используется `smb://` протокол, путь без лишних слешей.
- Время на сервере и клиенте должно быть синхронизировано (SMB требует этого).
### БД SQLite блокирована
- При конкурентных запросах SQLite может выдавать `database is locked`. Для веб-сервера с одним процессом это маловероятно. Если возникает — используйте `sqlite://:memory:` для тестов или настройте WAL-режим.
### Высокое потребление памяти
- Система спроектирована так, чтобы минимизировать удержание данных. Если вы наблюдаете рост памяти, убедитесь, что вы используете последнюю версию. При работе с очень большими таблицами (сотни тысяч строк) может потребоваться увеличить лимит строк в `processor.rs` (константа 10000).
## Разработка
Все статические ресурсы (HTML, CSS, шрифты Font Awesome, логотип, favicon) встроены в бинарник с помощью `include_str!` и `include_bytes!`. Для разработки можно редактировать файлы в `src/static/`, но при сборке они компилируются внутрь исполняемого файла.
### Сборка для production
cargo build --release
strip target/release/ae_anons # уменьшает размер
Итоговый бинарник можно переносить на любой Linux-сервер без дополнительных зависимостей (кроме libc и openssl, если не используется статическая сборка).
## Лицензия
**AE Anons** — MIT
**Nexrender** — MIT
**Font Awesome Free** — CC BY 4.0 (иконки) и SIL OFL 1.1 (шрифты)
---
Made with 🦀 Rust and ☕ coffee