316 lines
23 KiB
Markdown
316 lines
23 KiB
Markdown
# AE Anons - Автономный генератор спортивных анонсов в After Effects
|
||
|
||
<!-- markdownlint-disable MD033 -->
|
||
<p align="left">
|
||
<img src="assets/logo.png" alt="AE Anons Logo" width="250"/>
|
||
</p>
|
||
<!-- markdownlint-enable MD033 -->
|
||
|
||
[](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
|