Files
WFL/README.md
2026-04-23 18:34:37 +03:00

772 lines
30 KiB
Markdown
Raw Permalink 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.

# ⚽ VMIX Football Broadcast System
<p align="center">
<img src="https://img.shields.io/badge/Python-3.10%2B-3776AB?logo=python&logoColor=white" alt="Python">
<img src="https://img.shields.io/badge/FastAPI-0.115+-009688?logo=fastapi&logoColor=white" alt="FastAPI">
<img src="https://img.shields.io/badge/PostgreSQL-15+-4169E1?logo=postgresql&logoColor=white" alt="PostgreSQL">
<img src="https://img.shields.io/badge/WebSocket-Realtime-7C3AED" alt="WebSocket">
<img src="https://img.shields.io/badge/vMix-Integration-FF6A00" alt="vMix">
<img src="https://img.shields.io/badge/status-active-success" alt="Status">
</p>
> Система управления футбольными трансляциями с интеграцией в **vMix**, админ-панелью, live-обновлением матчей, генерацией данных для графики и WebSocket-взаимодействием с операторскими клиентами.
---
## 📌 Что это за проект
Проект предназначен для подготовки и сопровождения футбольных трансляций. Он объединяет в одном приложении:
- администрирование матчей, команд, игроков, тренеров, судей и стадионов;
- работу с составами и формациями на матч;
- генерацию данных для эфирной графики в vMix;
- создание и выгрузку `.vmix`-проекта;
- приём и отправку команд в vMix-клиенты через WebSocket;
- фоновое отслеживание матчей и обновление live-данных;
- синхронизацию расписания, игроков, команд и турнирной таблицы.
По текущей структуре проект ориентирован на внутреннюю production/операторскую эксплуатацию, а не на публичный API-сервис.
---
## ✨ Основные возможности
### Админка и матч-центр
- авторизация администратора;
- список матчей и выбор матча для сессии;
- рабочее пространство матча;
- редактирование составов;
- редактирование формаций;
- управление судьями;
- управление событиями матча.
### База футбольных сущностей
- игроки;
- команды;
- тренеры;
- судьи;
- стадионы;
- турнирная таблица;
- составы на матч;
- матчевые сессии для операторов.
### Интеграция с vMix
- генерация `.vmix`-проекта;
- формирование имени файла проекта;
- генерация JSON-данных для графики;
- публикация команд в подключённые vMix-клиенты;
- получение динамических значений из vMix API.
### Live-логика
- планировщик, который отслеживает матчи дня;
- запуск watcher-потоков перед матчем;
- опрос live-данных каждые 60 секунд;
- обновление статуса матча и счёта;
- обновление турнирной таблицы.
### Парсинг и синхронизация
- парсинг матчей;
- парсинг игроков;
- парсинг расписания;
- парсинг таблицы;
- синхронизация сущностей в БД через сервисный слой.
---
## 🏗 Архитектура
Проект построен вокруг классической схемы:
```text
Parsers -> Services -> Repositories -> PostgreSQL
|
+-> vMix JSON / .vmix generation
+-> FastAPI admin UI
+-> WebSocket command bus
+-> Scheduler / live workers
```
### Слои проекта
#### `parsers/`
Получают данные из внешних источников. Используются для матчей, игроков, расписания, команд и таблиц.
#### `services/`
Содержат прикладную бизнес-логику: синхронизацию данных, авторизацию, подготовку JSON для vMix.
#### `repositories/`
Отвечают за доступ к PostgreSQL. Здесь собраны CRUD-операции и специализированные запросы.
#### `templates/` + `static/`
HTML-шаблоны Jinja2, CSS, JS и статические файлы интерфейса.
#### `vmix/`
Логика генерации vMix-проекта и служебные функции, связанные с эфирной интеграцией.
#### `scheduler.py`
Фоновый мониторинг матчей и обновление live-состояния.
#### `agent.py`
Отдельный клиент для связи между vMix и сервером через WebSocket.
---
## 📁 Структура проекта
```text
vmix/
├── app.py # Основной FastAPI сервер
├── agent.py # WebSocket/vMix агент
├── db.py # Подключение к PostgreSQL
├── scheduler.py # Фоновый live scheduler
├── main.py # Demo/seed сценарий
├── deploy.sh # Скрипт деплоя
├── requirements.txt # Python зависимости
├── wfl.sql # Основная схема БД
├── parsers/
│ ├── parser_game.py # Парсинг/обновление данных матча
│ ├── parser_players.py # Парсинг игроков
│ ├── parser_schedule.py # Парсинг расписания
│ ├── parser_standings.py # Парсинг таблицы
│ └── parser_teams.py # Парсинг команд
├── repositories/
│ ├── audit_log_repository.py
│ ├── auth_repository.py
│ ├── coach_repository.py
│ ├── match_clock_repository.py
│ ├── match_coach_repository.py
│ ├── match_event_repository.py
│ ├── match_formation_repository.py
│ ├── match_lineup_repository.py
│ ├── match_referee_repository.py
│ ├── match_repository.py
│ ├── match_session_repository.py
│ ├── match_view_repository.py
│ ├── player_repository.py
│ ├── referee_repository.py
│ ├── stadium_repository.py
│ ├── standings_repository.py
│ ├── team_coach_repository.py
│ ├── team_repository.py
│ └── team_squad_repository.py
├── services/
│ ├── auth_service.py
│ ├── game_service.py
│ ├── players_service.py
│ ├── schedule_service.py
│ ├── standings_service.py
│ ├── teams_service.py
│ └── vmix_json_service.py
├── sql/
│ └── 001_auth.sql # Таблицы админ-пользователей и сессий
├── static/
│ ├── script.js
│ ├── styles.css
│ ├── smith.ico
│ └── images/
│ └── vmix_icon.png
├── templates/
│ ├── login.html
│ ├── matches.html
│ ├── match_workspace.html
│ ├── download_vmix.html
│ ├── admin_db_index.html
│ ├── admin_db_players.html
│ ├── admin_db_player_edit.html
│ ├── admin_db_referees.html
│ ├── admin_db_referee_edit.html
│ ├── admin_db_coaches.html
│ ├── admin_db_coach_edit.html
│ ├── admin_db_stadiums.html
│ ├── admin_db_stadium_edit.html
│ ├── admin_db_teams.html
│ └── admin_db_team_edit.html
└── vmix/
├── __init__.py
└── vmix_service.py # Генерация .vmix файла
```
---
## 🧰 Технологии
| Категория | Технологии |
|---|---|
| Backend | FastAPI |
| Валидация | Pydantic |
| Шаблоны | Jinja2 |
| База данных | PostgreSQL + psycopg2 |
| Парсинг | requests + BeautifulSoup4 |
| Realtime | WebSocket |
| Интеграция с эфиром | vMix API + XML |
| Конфигурация | python-dotenv |
| Дополнительно | numpy |
### Зависимости из `requirements.txt`
```txt
fastapi>=0.115,<1.0
uvicorn[standard]>=0.30,<1.0
pydantic>=2.0,<3.0
jinja2>=3.1,<4.0
python-multipart>=0.0.9,<1.0
requests>=2.31,<3.0
websockets>=12,<16
beautifulsoup4>=4.12,<5.0
psycopg2-binary>=2.9,<3.0
python-dotenv>=1.0,<2.0
numpy>=1.26,<3.0
```
Также в файле указан дополнительный приватный индекс и пакет `nasio`, поэтому для полноценной установки в целевой среде может понадобиться доступ к внутреннему package registry.
---
## 🚀 Быстрый старт
## 1. Клонирование репозитория
```bash
git clone <YOUR_REPOSITORY_URL>
cd vmix
```
## 2. Установка зависимостей
```bash
pip install -r requirements.txt
```
## 3. Настройка переменных окружения
Создай файл `.env` в корне проекта:
```env
DB_HOST=localhost
DB_PORT=5432
DB_NAME=wfl_db
DB_USER=postgres
DB_PASSWORD=your_password
```
`db.py` загружает эти параметры через `python-dotenv` и использует их для подключения к PostgreSQL.
## 4. Инициализация базы данных
Сначала разверни основную схему:
```bash
psql -U postgres -d wfl_db -f wfl.sql
```
Затем создай таблицы авторизации:
```bash
psql -U postgres -d wfl_db -f sql/001_auth.sql
```
## 5. Создание администратора
```bash
python scripts/create_admin.py
```
Скрипт запросит:
- `Username`
- `Password`
После этого создаст запись в таблице `admin_users`.
## 6. Запуск приложения
```bash
uvicorn app:app --host 0.0.0.0 --port 8000 --reload
```
После запуска приложение будет доступно по адресу:
```text
http://localhost:8000
```
---
## 🔐 Авторизация
В проекте используется сессионная авторизация для админки.
### Что есть в коде
- таблица `admin_users`;
- таблица `auth_sessions`;
- проверка пароля в `services/auth_service.py`;
- cookie/session-based доступ к админским маршрутам;
- logout с отзывом сессии.
### SQL-таблицы авторизации
Файл `sql/001_auth.sql` создаёт:
- `admin_users`
- `auth_sessions`
- индексы по токену, пользователю и активности
### Скрипт создания пользователя
`python scripts/create_admin.py`
---
## 🖥 Интерфейс и рабочие зоны
### Основные страницы
- `/login` — вход администратора;
- `/` — редирект/входная точка;
- `/admin/matches` — список матчей для администратора;
- `/admin/session/{session_token}` — рабочее пространство матча;
- `/admin/session/{session_token}/download-vmix-page` — страница выгрузки проекта;
- `/admin/db` — индекс админского раздела БД.
### Разделы админ-БД
- игроки;
- судьи;
- тренеры;
- стадионы;
- команды.
Для каждой сущности есть минимум список и форма редактирования.
---
## 📡 HTTP и WebSocket API
Ниже — маршруты, которые реально определены в `app.py`.
### Аутентификация
| Метод | Маршрут | Назначение |
|---|---|---|
| GET | `/login` | страница входа |
| POST | `/login` | авторизация |
| POST | `/logout` | выход |
### Матчи и сессии
| Метод | Маршрут | Назначение |
|---|---|---|
| GET | `/admin/matches` | список матчей |
| GET | `/admin/matches/{match_id}/select` | выбор матча и создание/открытие сессии |
| GET | `/admin/session/{session_token}` | рабочее пространство матча |
| POST | `/admin/session/{session_token}/load-match-data` | загрузка/обновление данных матча |
| POST | `/admin/session/{session_token}/close` | закрытие сессии |
| GET | `/admin/session/{session_token}/download-vmix-page` | HTML-страница выгрузки vMix |
| GET | `/admin/session/{session_token}/download-vmix` | скачивание `.vmix` |
### Формации и составы
| Метод | Маршрут | Назначение |
|---|---|---|
| POST | `/admin/session/{session_token}/formations/apply` | применить preset формации к игрокам |
| POST | `/admin/session/{session_token}/formations/save` | сохранить формации |
| GET | `/admin/session/{session_token}/squad-editor-data` | получить данные редактора состава |
| POST | `/admin/session/{session_token}/squad-editor-save` | сохранить состав |
| POST | `/admin/session/{session_token}/referees/save` | сохранить судей на матч |
### События матча
| Метод | Маршрут | Назначение |
|---|---|---|
| GET | `/admin/session/{session_token}/events` | список событий |
| POST | `/admin/session/{session_token}/event` | создать событие |
| PUT | `/admin/session/{session_token}/event/{event_id}` | обновить событие |
| DELETE | `/admin/session/{session_token}/event/{event_id}` | удалить событие |
| DELETE | `/admin/session/{session_token}/events` | очистить все события |
### Справочники БД
| Метод | Маршрут | Назначение |
|---|---|---|
| GET | `/admin/db` | главная админ-БД |
| GET/POST | `/admin/db/players` / `/admin/db/players/{player_id}/edit` | список/редактирование игроков |
| GET/POST | `/admin/db/referees` / `/admin/db/referees/{referee_id}/edit` | список/редактирование судей |
| GET/POST | `/admin/db/coaches` / `/admin/db/coaches/{coach_id}/edit` | список/редактирование тренеров |
| GET/POST | `/admin/db/stadiums` / `/admin/db/stadiums/{stadium_id}/edit` | список/редактирование стадионов |
| GET/POST | `/admin/db/teams` / `/admin/db/teams/{team_id}/edit` | список/редактирование команд |
### vMix JSON endpoints
| Метод | Маршрут | Назначение |
|---|---|---|
| GET | `/vmix/session/{session_token}/home-lineup` | стартовый состав хозяев |
| GET | `/vmix/session/{session_token}/away-lineup` | стартовый состав гостей |
| GET | `/vmix/session/{session_token}/home-bench` | запасные хозяев |
| GET | `/vmix/session/{session_token}/away-bench` | запасные гостей |
| GET | `/vmix/session/{session_token}/info` | информация о матче |
| GET | `/vmix/session/{session_token}/standings` | турнирная таблица |
| GET | `/vmix/session/{session_token}/schedule` | расписание/тур |
| GET | `/vmix/session/{session_token}/home-formations` | формация хозяев |
| GET | `/vmix/session/{session_token}/away-formations` | формация гостей |
| GET | `/vmix/session/{session_token}/scoreboard` | данные для счётчика |
| GET | `/vmix/session/{session_token}/match-events` | данные по событиям матча |
### Команды для vMix-клиентов
| Метод | Маршрут | Назначение |
|---|---|---|
| POST | `/api/vmix/publish-command` | отправить команду в vMix-клиент(ы) |
| GET | `/api/vmix/clients` | список подключённых клиентов |
| WS | `/ws/vmix-client` | WebSocket-канал клиентов |
---
## 🎬 Интеграция с vMix
### Что делает сервер
Сервер:
- создаёт матчевые сессии;
- готовит JSON для графики;
- собирает и отдаёт `.vmix`-проект;
- отправляет команды в подключённые клиентские агенты.
### Что делает агент `agent.py`
Агент — это отдельное приложение, которое запускается рядом с vMix и выполняет роль моста между сервером и локальным vMix API.
#### В коде агента есть:
- `VMIX_API = "http://127.0.0.1:8088/api"`
- `WS_BASE = "wss://wfl.tvstart.ru/ws/vmix-client"`
- чтение XML-ответа vMix;
- извлечение `dynamic/value1`, `dynamic/value2` и других значений;
- регистрация клиента в WebSocket;
- reconnect-логика.
### Как это работает в общем
1. Оператор открывает матч в админке.
2. Создаётся матчевая сессия с `session_token`.
3. Сервер отдаёт JSON и/или `.vmix`-файл.
4. Локальный агент подключается по WebSocket.
5. Сервер отправляет команды конкретному клиенту, группе или оператору.
6. Агент применяет команды в локальном vMix через HTTP API.
---
## 🧾 Генерация JSON для графики
Логика сосредоточена в `services/vmix_json_service.py`.
### По коду видно, что сервис формирует:
- стартовые составы;
- запасных;
- информацию о матче;
- турнирную таблицу;
- расписание тура;
- формации команд;
- scoreboard-информацию;
- события матча;
- данные для показа авторов голов.
### Особенности реализации
- строятся полные имена игроков;
- добавляются суффиксы капитана и вратаря;
- формируются пути к фотографиям игроков;
- используются логотипы команд и специальные варианты логотипов для некоторых клубов;
- матчевая информация собирается SQL-запросами по сессии.
---
## 📦 Генерация `.vmix` проекта
Файл `vmix/vmix_service.py` используется для:
- сборки бинарного содержимого vMix-проекта;
- генерации имени файла;
- выдачи файла пользователю через FastAPI.
В `app.py` для этого используются:
- `build_vmix_project_bytes`
- `build_vmix_filename`
Это означает, что оператор может открыть страницу сессии и скачать готовый `.vmix`-проект под конкретный матч.
---
## ⏱ Scheduler и live-мониторинг
`scheduler.py` — это фоновый процесс, который поднимается на старте приложения:
```python
@app.on_event("startup")
def start_scheduler():
import threading
thread = threading.Thread(target=run_scheduler, daemon=True)
thread.start()
```
### Ключевые интервалы
```python
MATCH_START_LEAD_MINUTES = 1
LIVE_MATCH_POLL_SECONDS = 60
MATCHES_LOOP_SECONDS = 60
STANDINGS_LOOP_SECONDS = 60
WORKER_ERROR_RETRY_SECONDS = 30
WORKER_MAX_ERRORS_IN_ROW = 20
```
### Что делает scheduler
- получает матчи на сегодня из БД;
- определяет, какие матчи уже пора мониторить;
- запускает отдельные worker-потоки;
- обновляет status/home_score/away_score в таблице матчей;
- вызывает обновление турнирной таблицы;
- ведёт реестр активных worker'ов.
### Источник live-данных
По текущему коду live-страница матча запрашивается с:
```text
https://wfl.rfs.ru/match/{match_id}
```
HTML разбирается через BeautifulSoup.
### Важное замечание
В `fetch_match_live_data()` есть незавершённая часть: функция частично реализована и в конце всё ещё содержит `NotImplementedError`. То есть scheduler уже встроен, но live-парсинг требует аккуратной доработки, чтобы считать его полностью production-ready.
---
## 🗄 Репозитории и доменные сущности
По составу `repositories/` проект работает со следующими сущностями:
### Пользователи и аудит
- `auth_repository.py`
- `audit_log_repository.py`
### Матчи
- `match_repository.py`
- `match_session_repository.py`
- `match_view_repository.py`
- `match_clock_repository.py`
- `match_event_repository.py`
### Составы и формации
- `match_lineup_repository.py`
- `match_formation_repository.py`
- `team_squad_repository.py`
### Официальные лица и стадионы
- `referee_repository.py`
- `match_referee_repository.py`
- `coach_repository.py`
- `match_coach_repository.py`
- `team_coach_repository.py`
- `stadium_repository.py`
### Команды, игроки, таблица
- `team_repository.py`
- `player_repository.py`
- `standings_repository.py`
Это хороший признак разделённой архитектуры: доступ к данным отделён от HTTP-слоя и от прикладной логики.
---
## 🔄 Сервисы синхронизации
В `services/` есть отдельные сервисы:
- `teams_service.py`
- `schedule_service.py`
- `players_service.py`
- `standings_service.py`
- `game_service.py`
- `auth_service.py`
- `vmix_json_service.py`
### Demo-режим
`main.py` показывает, как можно программно наполнить систему тестовыми данными:
- создаются 2 команды;
- создаётся матч;
- добавляются игроки;
- заполняется турнирная таблица.
Запуск:
```bash
python main.py
```
Это удобно для первичной проверки БД и шаблонного сценария разработки.
---
## 🧪 Полезные команды
### Проверка подключения к БД
```bash
python db.py
```
### Создание администратора
```bash
python scripts/create_admin.py
```
### Demo-наполнение
```bash
python main.py
```
### Запуск сервера
```bash
uvicorn app:app --reload
```
---
## 🖼 Скриншоты
Ниже заготовки под реальные скриншоты интерфейса. Когда будут реальные изображения, просто положи их в репозиторий и обнови пути.
```md
![Login](docs/screens/login.png)
![Matches](docs/screens/matches.png)
![Workspace](docs/screens/workspace.png)
![Database](docs/screens/database.png)
```
---
## ⚠️ Ограничения и важные замечания
### 1. Документация OpenAPI отключена
В `app.py` явно выключены:
- `/docs`
- `/redoc`
- `/openapi.json`
Это нормально для внутреннего production-режима, но неудобно для внешних интеграций.
### 2. Live scheduler требует доработки
`fetch_match_live_data()` ещё не завершена до конца и сейчас выглядит как заготовка с частичной логикой.
### 3. Есть Windows-специфичные элементы
В JSON-сервисе и агенте встречаются Windows-пути и логика работы с иконкой консоли. Это значит, что часть инфраструктуры ориентирована на Windows-машину оператора.
### 4. Есть внешняя зависимость от приватного Python registry
В `requirements.txt` указан `--extra-index-url`, поэтому разворачивание в другой среде может потребовать доступов.
### 5. Есть жёстко заданные интеграционные значения
Например:
- `http://127.0.0.1:8088/api`
- `wss://wfl.tvstart.ru/ws/vmix-client`
Для production лучше вынести их в `.env`.
---
## 🔮 Что можно улучшить
### Инфраструктура
- добавить Dockerfile и `docker-compose.yml`;
- вынести конфигурацию целиком в `.env`;
- добавить healthcheck и monitoring;
- настроить CI/CD.
### Backend
- включить структурированное логирование;
- покрыть ключевые места тестами;
- заменить часть threading-логики на более управляемый background orchestration;
- валидацию live-парсеров сделать устойчивее.
### Безопасность
- ограничить rate limit для логина;
- добавить CSRF-защиту, если нужна;
- вынести секреты и URL в окружение;
- расширить аудит действий администратора.
### API и DX
- вернуть внутреннюю OpenAPI-документацию под флагом окружения;
- добавить Postman/Insomnia collection;
- оформить контракт JSON-ответов для графики.
### UI/UX
- добавить больше навигации в админке;
- улучшить визуальный статус live-сессий;
- добавить быстрые действия в матч-центре.
---
## 🛠 Пример локального сценария запуска
```bash
# 1. установить зависимости
pip install -r requirements.txt
# 2. создать .env
cp .env.example .env # если появится шаблон
# 3. поднять БД и применить SQL
psql -U postgres -d wfl_db -f wfl.sql
psql -U postgres -d wfl_db -f sql/001_auth.sql
# 4. создать администратора
python scripts/create_admin.py
# 5. при необходимости заполнить демо-данными
python main.py
# 6. запустить сервер
uvicorn app:app --host 0.0.0.0 --port 8000 --reload
```
---
## 🤝 Вклад в проект
Если проект будет развиваться дальше, рекомендуется принять минимальные правила contribution:
1. отдельная ветка на каждую фичу;
2. PR с описанием изменений;
3. проверка SQL-миграций перед merge;
4. ручная проверка vMix-сценария и матчевого workflow.
---
## 📄 Лицензия
В текущем архиве лицензия отдельно не обнаружена.
Если проект внутренний — можно оставить private/internal use only.
Если репозиторий будет публичным — добавь подходящую лицензию, например MIT, Apache-2.0 или другую нужную команде.
---
## 👨‍💻 Итог
Этот проект — это не просто CRUD-приложение, а полноценный операторский инструмент для футбольной трансляции:
- FastAPI-сервер с HTML-интерфейсом;
- PostgreSQL как основной источник данных;
- сессионная админ-авторизация;
- матчевые рабочие сессии;
- составы, формации, события и судьи;
- JSON и `.vmix` для эфирной графики;
- WebSocket-мост для операторских клиентов;
- scheduler для live-матчей и обновления данных.
Если нужно, следующим сообщением я могу так же сделать ещё и:
- `README_EN.md` на английском;
- `docs/API.md` с описанием всех маршрутов;
- `docker-compose.yml` для локального запуска;
- `.env.example`;
- более «маркетинговую» GitHub-версию с centered hero, anchors и navigation block.