# ⚽ 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.
