SoccerLife AI Assist перехватывает и анализирует данные матча с soccerlife.ru в реальном времени. Данные поступают из двух источников:
Browser soccerlife.ru
│
├── GET /game.php?id=XXXXX ──► HTML-страница матча
│ └── переменная _inf (JSON)
│ ├── Состав обеих команд с side=1/2
│ ├── Цвета формы, имена, номера
│ └── Метаданные матча (время старта, минута)
│
└── GET /ajax/moves.php?game_id=X&minute=Y ──► JSON
├── Позиции всех игроков (каждый ~1с)
├── Траектория мяча
├── Позиция арбитра + игровые события
└── Покрывает следующие 5 игровых минут
GET https://soccerlife.ru/game.php?id={game_id}
HTML-страница. Содержит встроенный JavaScript с переменной _inf
(подробнее в разделе 3). Авторизация не требуется для просмотра.
GET https://soccerlife.ru/ajax/moves.php?game_id={game_id}&minute={minute}
| Параметр | Тип | Описание |
|---|---|---|
game_id | int | ID матча из URL |
minute | int 0–90 | Стартовая игровая минута блока (кратно 5) |
Возвращает JSON с позициями всех сущностей на следующие ~5 игровых минут.
Ответ:
{
"status": 1, // 1 = OK
"next_minute": 41, // когда запрашивать следующий блок
"check_time": 1774698554, // unix timestamp сервера
"time": 1774698599, // время обработки запроса
"moves": {
"ENTITY_ID": ["frame1", "frame2", ...],
...
}
}
GET https://soccerlife.ru/ajax/game4.php?action=live_results&game_id={id}&type=1
Возвращает HTML-таблицу с результатами других матчей, идущих параллельно в том же чемпионате. Полезно для отслеживания турнирной ситуации.
_inf — метаданные матчаВстроена в HTML как _inf = {...} (второе присваивание — JSON-формат).
| Поле | Тип | Описание |
|---|---|---|
start | string (unix ts) | Время начала матча |
duration | int | Длительность в секундах (2040 ≈ 34 мин) |
in_progress | bool | Идёт ли матч сейчас |
minute | int | Текущая игровая минута |
participants | object | Все сущности матча (см. ниже) |
lineup | int | Флаг состава (0/1) |
hls | array | HLS-стримы (обычно пусто) |
game_url | string | URL матча |
participants[PLAYER_ID]{
"type": 1, // 1=игрок, 2=мяч (entity "7"), 3=арбитр (entity "89")
"side": 1, // 1=хозяева, 2=гости
"kit_id": 3629, // ID комплекта формы
"kit_color": "#4747eb", // основной цвет формы
"text_color": "#fff", // цвет номера
"circle_color": "#fff", // цвет подложки
"position": "AM", // амплуа (GK, CD, DM, CM, AM, ST, LD, RD, LW, RW, LM, RM, LB, RB)
"name": "Йилмаз", // фамилия игрока
"number": "90" // номер на спине
}
side — единственный надёжный источник для разделения команд.
В данных moves.php все игроки кодируются одинаково и команду из них не определить.
Дополнительно из JS-переменной game_inf:
| Поле | Описание |
|---|---|
id1 | Roster ID команды хозяев (ссылка на /roster.php?id=) |
id2 | Roster ID команды гостей |
unix | Unix timestamp старта матча |
hp_finish | Unix timestamp предполагаемого конца матча |
techdef | Техническое поражение: -1=нет, 1=хозяева, 2=гости |
moves| Ключ | Тип сущности | Описание |
|---|---|---|
"7" | Мяч | Позиция и траектория мяча. Команда = 0 |
"89" | Арбитр | Позиция арбитра + текстовые аннотации игровых событий |
"XXXXXXX" | Игрок | 6-8 значный ID игрока |
Каждый элемент массива — строка вида:
"duration:action:x:y:direction[:label]"
| Поле | Тип | Описание |
|---|---|---|
duration | float | Длительность кадра в секундах анимации |
action | int | Код действия (см. раздел 5) |
x | float | Координата X на поле (0–104) |
y | float | Координата Y на поле (0–69). -10 = вне поля (запас) |
direction | int | Для игроков: направление спрайта (1–4). В ПЕРВОМ кадре (action=69): номер команды (0/1/2). Для мяча: всегда 0. |
label | string? | Опциональная текстовая аннотация события |
"0:69:39:28:2:am" → action=69, начальная позиция, команда=2, амплуа=am "699.2:69:52:-10:2" → запасной игрок (y=-10)
duration
(~700–1300с) — это серверный плейсхолдер, не реальное время.
Все временны́е расчёты ведутся начиная со второго кадра (elapsed=0).
| Код | Название | Описание |
|---|---|---|
69 | INITIAL | Начальный кадр позиции. Только первый кадр каждой сущности. Duration — плейсхолдер |
1 | JUMP/KICK | Короткий стоп/прыжок/контакт с мячом. Игрок останавливается |
| Код | Описание |
|---|---|
5 | Бег/ходьба — основное движение по полю |
20 | Сильный удар / длинный пас |
4 | Падение/подкат/столкновение |
| Код | Направление | Описание |
|---|---|---|
2 | ↙ / ↖ | Качение по земле (левое направление спрайта) |
4 | ↗ / ↘ | Качение по земле (правое направление спрайта) |
3 | Высокая | Воздушная траектория — удар, навес, заброс, вбрасывание из аута |
20 | Дальняя | Мощный удар / выбивание вратарём |
22 | Рестарт | Мяч установлен в точке возобновления игры (угловой, штрафной, от ворот) |
1 | Стоп | Мяч остановился / минимальное движение |
Entity "7" содержит полную траекторию мяча каждого 5-минутного блока.
Анализируя её, можно определить:
| Ситуация | Признак |
|---|---|
| Мяч вышел за линию | y=-10 или x=-10 (вне поля) |
| Рестарт игры | action=22 после позиции вне поля |
| Удар / навес | action=3 (воздушная траектория) |
| Сильный удар | action=20 |
| Владение у хозяев | avg_x мяча < 52 |
| Владение у гостей | avg_x мяча > 52 |
| Атака хозяев | мяч движется к x≈99 (ворота гостей) |
| Атака гостей | мяч движется к x≈9 (ворота хозяев) |
"699.4:69:52:4:0" — стартовая позиция, центр поля "0.9:2:58:4:0" — качение вправо "1:4:58:10:0" — качение вправо вверх "1.3:3:39:4:0" — воздушная траектория (навес/удар) "0.8:2:-10:26:0" — мяч вышел за линию (x=-10) "7.7:20:-10:26:0" — мяч ещё вне поля (вратарь берёт) "0:22:6:42:0" — мяч установлен у ворот (от ворот / угловой) "1:20:6:42:0" — удар от ворот
Метки-события появляются только у entity "89" (арбитр).
Игроки в moves.php меток событий не имеют — только арбитр объявляет события.
| Метка (Russian) | event_type | Иконка | Описание |
|---|---|---|---|
ГОЛ! | goal | ⚽ | Гол. Сервер шлёт 3 раза подряд — дедуплицируется |
штрафной | free_kick | 🟡 | Штрафной удар |
угловой | corner | 🔵 | Угловой удар |
вне игры | offside | 🚩 | Офсайд (также офсайд) |
от ворот | goal_kick | 🥅 | Удар от ворот |
в аут | throw_in | ↩️ | Вбрасывание из аута |
пенальти | penalty | 🔴 | Пенальти |
жёлтая | yellow_card | 🟨 | Жёлтая карточка (также *yc*) |
красная | red_card | 🟥 | Красная карточка |
замена | substitution | 🔄 | Замена игрока |
фол | foul | 🦵 | Нарушение |
устное пр. | verbal_warning | 🗣️ | Устное предупреждение |
на грани | dangerous_tackle | ⚠️ | Опасная игра |
штанга | crossbar | 🔔 | Попадание в штангу/перекладину |
поехали! | kickoff | ▶️ | Начало / рестарт после гола (старт) |
В moves.php нет прямой метки «кто забил». Применяем эвристику:
| Метод | URL | Описание |
|---|---|---|
| GET | / |
Главная страница с интерфейсом |
| GET | /docs |
Эта страница документации |
| GET | /info/{game_id} |
Метаданные матча: составы, цвета, текущая минута |
| GET | /chunk/{game_id}/{minute} |
Одиночный разбор чанка в JSON (для отладки) |
| GET | /stream/{game_id}?minute=N&cookie=... |
SSE-стрим: живые данные матча. События приходят в реальном времени |
/stream/{game_id}| event | Когда | Payload |
|---|---|---|
game_info | Один раз при старте | Составы, цвета, текущая минута |
chunk | Каждые ~5 мин | Полные данные блока: игроки, мяч, все события |
game_event | В реальном времени | Одно событие в момент его наступления |
heartbeat | Каждые 15с | Пинг для поддержания соединения |
error | При ошибке | Сообщение об ошибке |
done | Конец матча | Матч завершён |
Запуск стрима (/stream/{game_id})
│
▼
fetch_game_info(game_id) ← GET game.php → _inf JSON
│
├── team membership (side 1/2)
├── player names & positions
└── kit colors
SSE → event: game_info ← клиент получает составы и цвета
│
└── LOOP: minute = start_minute → 90
│
▼
fetch moves.php?minute=N
│
▼
parse_chunk()
├── _parse_entity() × 40 сущностей
│ ├── Первый кадр (action=69) → стартовая позиция, амплуа
│ └── Остальные кадры → анимация, duration без initial frame
│
└── _extract_events()
├── Пропускаем initial frame (duration — плейсхолдер)
├── Собираем labeled frames от entity "89"
├── Пропускаем метки амплуа (cd, dm, gk...) как non-events
└── Дедуплицируем повторяющиеся события (< 10с интервал)
SSE → event: chunk ← клиент рендерит игроков на поле
│ и показывает все события как "pending"
│
└── LOOP: событие за событием
│
▼
sleep(event.second - elapsed)
│
▼
SSE → event: game_event ← клиент активирует pending → fired
| Файл | Роль |
|---|---|
models.py | Датаклассы: MoveFrame, EntityTrack, GameEvent, MatchChunk. EVENT_MAP, Action коды |
parser.py | Разбор строк moves.php, извлечение событий, дедупликация |
game_info.py | Парсер _inf из game.php — составы, команды, цвета |
fetcher.py | HTTP-клиент, LiveMatchPoller (background thread), CLI |
web_app.py | FastAPI: SSE-стрим, сериализация, определение скорера гола |
main.py | CLI: одиночный разбор или живой режим в терминале |
static/index.html | SPA: SVG-поле, лента событий, анимация, pending/fired система |