# ⚽ VMIX Football Broadcast System

Python FastAPI PostgreSQL WebSocket vMix Status

> Система управления футбольными трансляциями с интеграцией в **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 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.