Загрузка данных


# Master cabinet and streaming

## User

1. Создай у модели Master поле "пароль". И его редактирование в админке. По умолчанию, оно генерируется автоматически.
2. Создай отдельную страницу на фронте (master/cabinet). Вход на нее, по паролю из п.1. Там мастер будет видеть все свои трансляции (модели Shedule, где поле master соответствует залогинившемуся мастеру и тип занятия - онлайн) в виде таблицы с названием и указанием дня недели и времени вещания. Если день и время соответствует текущему, то есть кнопка "начать трансляцию"
3. Попадая на нее, начинается прямо в браузере видео трансляция через наш сервер.
4. Также создай отдельную временную страницу (нигде в меню не появляющуюся) со списком всех онлайн занятий Shedule  И кнопкой "присоединиться к трансляции". При клике на нее любой человек попадает на страницу, где видит идущую трансляцию из п.3

## Assistant

Разберу структуру моделей Master/Schedule и админки, затем добавлю пароль, кабинет мастера и страницы трансляции.

### Tool Todo Write

### Tool New agent

### Tool Get Mcp Tools

```
{"server":"user-context7","toolCallId":"call-2c1137fa-fbe8-449a-9505-5c20129e01be-2\nfc_c088a317-495c-9bc6-9fa9-408c183f8fb2_2"}
```

## Assistant

Проверяю модели, админку и наличие медиа-сервера для стриминга.

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Models\\Master.php"}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Http\\Controllers\\Admin\\MasterAdminController.php"}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\admin\\masters\\form.php"}
```

### Tool Ripgrep Raw Search

```
{"pattern":"mediamtx|webrtc|rtmp|livekit|janus|mediasoup|hls|streaming|whip|whep","path":"c:\\OSPanel\\home\\inbi.loc","caseInsensitive":true,"multiline":false,"toolCallId":"call-9552df08-6cee-4dab-a55e-847a573ca651-6\nfc_0696738f-b2c7-97a1-9ee6-a9500b612f1b_3","offset":0}
```

### Tool Ripgrep Raw Search

```
{"pattern":"Master|Schedule|cabinet|master","path":"c:\\OSPanel\\home\\inbi.loc\\routes\\web.php","caseInsensitive":false,"multiline":false,"toolCallId":"call-9552df08-6cee-4dab-a55e-847a573ca651-7\nfc_0696738f-b2c7-97a1-9ee6-a9500b612f1b_4","offset":0}
```

### Tool Glob File Search

```
{"targetDirectory":"c:\\OSPanel\\home\\inbi.loc","globPattern":"**/docker*"}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Models\\Schedule.php","limit":200}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\routes\\web.php","offset":1,"limit":100}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\composer.json","limit":80}
```

### Tool Glob File Search

```
{"targetDirectory":"c:\\OSPanel\\home\\inbi.loc","globPattern":"**/.env*"}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\layouts\\header.php","limit":80}
```

### Tool Ripgrep Raw Search

```
{"pattern":"getDay|day_of_week|start_time","path":"c:\\OSPanel\\home\\inbi.loc\\app\\Models\\Schedule.php","caseInsensitive":false,"multiline":false,"toolCallId":"call-91c1e96b-436c-4e7c-8546-f080f4b45a40-14\nfc_b9dff7bc-0e73-9202-b215-1fa82e1f1670_5","offset":0}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\bootstrap\\app.php"}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\auth\\login.php","limit":80}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\routes\\web.php","offset":280,"limit":50}
```

### Tool Glob File Search

```
{"targetDirectory":"c:\\OSPanel\\home\\inbi.loc","globPattern":"**/Middleware/*.php"}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\.env.example","limit":80}
```

### Tool Mcp

```
{"name":"user-context7-resolve-library-id","args":{"libraryName":"Laravel","query":"Laravel 12 migration model middleware session authentication"},"toolCallId":"call-dbf82417-b015-45b9-96d4-4e820f547e4a-20\nfc_caa68e10-e000-9a4d-bdb2-f7aeb88fc39e_5","providerIdentifier":"context7","toolName":"resolve-library-id","smartModeApprovalOnly":false,"skipApproval":false}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Http\\Middleware\\UserAuth.php"}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\config\\app.php","limit":100}
```

### Tool Ripgrep Raw Search

```
{"pattern":"timezone|Europe/Moscow","path":"c:\\OSPanel\\home\\inbi.loc\\config","caseInsensitive":false,"multiline":false,"toolCallId":"call-eb062170-903b-4106-ab84-2a7cfbe14222-23\nfc_4b31275a-ffe5-901f-aae1-527c15ef4360_2","offset":0}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\cabinet\\index.php","limit":60}
```

### Tool Glob File Search

```
{"targetDirectory":"c:\\OSPanel\\home\\inbi.loc","globPattern":"**/migrations/*masters*"}
```

### Tool Mcp

```
{"name":"user-context7-query-docs","args":{"libraryId":"/websites/laravel_12_x","query":"How to create a migration to add a column, hash passwords with Hash facade, and register custom middleware alias"},"toolCallId":"call-eb062170-903b-4106-ab84-2a7cfbe14222-26\nfc_4b31275a-ffe5-901f-aae1-527c15ef4360_5","providerIdentifier":"context7","toolName":"query-docs","smartModeApprovalOnly":false,"skipApproval":false}
```

## Assistant

Реализую пароль мастера, кабинет и WebRTC-трансляцию с сигналингом через Laravel.

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\database\\migrations\\2026_01_29_120000_add_notes_to_masters_table.php"}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Models\\Schedule.php","offset":200}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\layouts\\footer.php","limit":40}
```

### Tool Ripgrep Raw Search

```
{"pattern":"btn more|section_standart|\\.table","path":"c:\\OSPanel\\home\\inbi.loc\\public\\src\\styles","glob":"*.css","caseInsensitive":false,"headLimit":30,"multiline":false,"toolCallId":"call-c111085b-8b07-4dae-b7fd-38c897948a4b-30\nfc_1d867c24-b1a6-999e-9951-112d4b7acdb8_3","offset":0}
```

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

## Assistant

Исправляю роут логина и создаю представления со стримингом.

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Ripgrep Raw Search

```
{"pattern":"view\\.|ViewService|Blade::|php.*engine","path":"c:\\OSPanel\\home\\inbi.loc\\app","caseInsensitive":false,"multiline":false,"toolCallId":"call-42d263c3-33d6-4c5a-9966-4b83dbcddae0-58\nfc_8947bc98-4f7f-9842-a860-006af133fd7c_0","offset":0}
```

### Tool Glob File Search

```
{"targetDirectory":"c:\\OSPanel\\home\\inbi.loc","globPattern":"**/AppServiceProvider.php"}
```

### Tool Ripgrep Raw Search

```
{"pattern":"\\.php|addExtension|view","path":"c:\\OSPanel\\home\\inbi.loc\\config","caseInsensitive":false,"multiline":false,"toolCallId":"call-42d263c3-33d6-4c5a-9966-4b83dbcddae0-60\nfc_8947bc98-4f7f-9842-a860-006af133fd7c_2","offset":0}
```

### Tool Edit File V2

### Tool Run Terminal Command V2

### Tool Todo Write

## Assistant

БД сейчас недоступна — попробую применить миграции через MySQL MCP и проверю код на ошибки.

### Tool Get Mcp Tools

```
{"server":"user-MySQL","toolCallId":"call-7539f1ce-2a92-4a7a-a5b7-9776c0ac4126-64\nfc_e0cced8e-68ce-9f42-a3ae-19b6735b2e70_0"}
```

### Tool Run Terminal Command V2

### Tool Edit File V2

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\routes\\web.php","offset":300,"limit":90}
```

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

## Assistant

Готово. Кратко по пунктам:

### 1. Пароль мастера
- Поле `cabinet_password` в модели `Master` (уникальное)
- При создании генерируется автоматически; в админке можно изменить или нажать «Сгенерировать»
- Миграция: `2026_07_22_000001_add_cabinet_password_to_masters_table.php` (для существующих мастеров пароли тоже проставятся)

### 2. Кабинет мастера
- `/master/cabinet/login` — вход по паролю
- `/master/cabinet` — таблица онлайн-занятий (`hall = Online`) с днём и временем
- Кнопка «Начать трансляцию» — в день занятия, с −15 мин до старта и до конца слота (часовой пояс `Europe/Moscow`)

### 3. Трансляция
- `/master/cabinet/broadcast/{id}` — камера/микрофон в браузере
- Сигналинг WebRTC через Laravel (cache); медиа — напрямую браузер↔браузер (STUN Google)
- За зрителями NAT может понадобиться свой TURN-сервер

### 4. Временная страница зрителей (без меню)
- `/online-streams` — все онлайн-занятия + «Присоединиться к трансляции»
- `/online-streams/{id}` — просмотр эфира (ждёт старта, если ещё не в эфире)

**Важно:** MySQL сейчас недоступен, миграции не применились. Когда БД будет запущена:

```bash
php artisan migrate
```

## User

При старте трансляции мастером, вроде как, появляется окно видео и камера активна, но в консоли браузера ошибки:


2
33:187 poll error OperationError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to parse SessionDescription. a=rtpmap:126 telephone-event/8000 Invalid SDP line.
    at handleOffer (33:134:18)
    at poll (33:172:27)
6
33:150 ICE add failed InvalidStateError: Failed to execute 'addIceCandidate' on 'RTCPeerConnection': The remote description was null
33:187 poll error OperationError: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to parse SessionDescription. a=rtpmap:126 telephone-event/8000 Invalid SDP line.
    at handleOffer (33:134:18)
    at poll (33:172:27)
6
33:150 ICE add failed InvalidStateError: Failed to execute 'addIceCandidate' on 'RTCPeerConnection': The remote description was null

А у клиента, который смотрит трансляцию, все застревает на "Подключение к трансляции…". Дальше трансляция не начинается, никаких ошибок в браузере нет

## Assistant

Ошибка парсинга SDP обычно из‑за порчи описания при передаче через JSON/cache. Исправлю сериализацию SDP и буферизацию ICE-кандидатов.

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\master\\cabinet\\broadcast.php"}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\online_streams\\watch.php"}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Services\\WebRtcSignalingService.php"}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Http\\Controllers\\MasterCabinetController.php","offset":130,"limit":80}
```

## Assistant

SDP ломается при сериализации через cache. Переведу сигналинг на явный JSON и нормализую SDP/ICE.

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

## Assistant

Для надёжности закодирую SDP в base64 — так переносы строк не смогут повредиться в cache/MySQL.

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

## Assistant

Причина была в повреждении SDP при проходе через PHP/cache: переносы строк ломались, Chrome не мог разобрать строку `a=rtpmap:126 telephone-event/8000`, handshake не завершался, зритель зависал на «Подключение…». Ошибки ICE шли уже как следствие.

Что изменено:
- SDP передаётся в **base64** (`sdp_b64`)
- сигналинг в cache хранится как **JSON**, а не PHP-serialize
- ICE-кандидаты **буферизуются**, пока не установлен remote description

Обновите обе страницы (hard refresh) и попробуйте снова: мастер начинает эфир → зритель заходит на `/online-streams/{id}`. У мастера в счётчике зрителей должно появиться `1`, у зрителя — «Вы в эфире».

## User

Теперь опиши, как реализована эта трансляция, с помощью каких технологий? Ее слабые стороны.

## Assistant

Сверю реализацию по коду и опишу архитектуру, технологии и ограничения.

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Services\\WebRtcSignalingService.php","limit":40}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Models\\LiveBroadcast.php"}
```

### Tool Ripgrep Raw Search

```
{"pattern":"RTCPeerConnection|WebRtc|live_broadcast","path":"c:\\OSPanel\\home\\inbi.loc","glob":"*.{php,js}","caseInsensitive":false,"headLimit":30,"multiline":false,"toolCallId":"tool_1736a574-dff2-4a63-83d6-bac48c903d0","offset":0}
```

## Assistant

## Как устроена трансляция

Это **не классический стриминг через медиасервер** (не RTMP, не HLS, не YouTube-подобный эфир). Реализован **WebRTC в браузере** с **P2P-моделью**: видео и аудио идут напрямую между браузером мастера и браузером зрителя. Laravel выступает только как **сигнальный сервер** и хранит состояние эфира.

```mermaid
sequenceDiagram
    participant M as Браузер мастера
    participant L as Laravel (HTTP + Cache)
    participant V as Браузер зрителя

    M->>L: POST /master/cabinet/broadcast/{id}/stop (старт эфира)
    L->>L: live_broadcasts (status=live)

    V->>L: GET /online-streams/{id}/status
    V->>L: POST signal (offer SDP)
    L->>L: Cache queue

    M->>L: GET signal/poll (каждые ~800ms)
    L-->>M: offer от зрителя
    M->>L: POST signal (answer + ICE)
    L-->>V: GET signal/poll → answer

    M<<->>V: WebRTC P2P (видео/аудио напрямую)
```

---

## Технологии

| Слой | Что используется |
|------|------------------|
| Фронт | Обычные PHP-views + vanilla JavaScript |
| Видео в браузере | **WebRTC API**: `getUserMedia`, `RTCPeerConnection` |
| Сигналинг | HTTP **long polling** (`/signal/poll` раз в ~800–1000 мс) |
| Обмен SDP/ICE | JSON через Laravel, SDP в **base64** (`sdp_b64`) |
| Очередь сигналов | **Laravel Cache** (`WebRtcSignalingService`) |
| Состояние эфира | MySQL, таблица `live_broadcasts` |
| NAT traversal | Только **STUN** (`stun:stun.l.google.com:19302`) |
| Авторизация мастера | Сессия `master_cabinet_id` по паролю `cabinet_password` |
| Зритель | Без авторизации, `viewer_id` = session id |

### Поток мастера
1. Вход в `/master/cabinet` по паролю.
2. «Начать трансляцию» → запись в `live_broadcasts`, страница `/master/cabinet/broadcast/{scheduleId}`.
3. Браузер запрашивает камеру/микрофон (`getUserMedia`).
4. На каждого зрителя — отдельный `RTCPeerConnection`, мастер отвечает на offer зрителя answer’ом.

### Поток зрителя
1. `/online-streams` → «Присоединиться» → `/online-streams/{scheduleId}`.
2. Ожидание `status: live`.
3. Создаётся `RTCPeerConnection` (только приём: `recvonly`).
4. Отправляется **offer**, по polling приходит **answer** и ICE-кандидаты.
5. После handshake видео идёт **напрямую с браузера мастера**.

---

## Что важно понимать

**Видео через Laravel не проходит.** Сервер не кодирует, не транскодирует и не раздаёт поток — только помогает браузерам договориться (SDP/ICE).

Это **mesh-топология**: один мастер → N отдельных P2P-соединений. Не SFU (Selective Forwarding Unit) и не MCU.

---

## Слабые стороны

### 1. Масштабируемость
- Каждый зритель = отдельное соединение у мастера.
- Камера/микрофон кодируются и отправляются N раз с одного ПК.
- На практике обычно **2–5 зрителей**, дальше нагрузка на CPU и сеть мастера растёт быстро.

### 2. NAT и сети без TURN
- Есть только STUN, **TURN-сервера нет**.
- За строгим NAT, корпоративным файрволом или мобильным CGNAT соединение часто **не устанавливается** — зритель зависает на «Подключение…» без явной ошибки.

### 3. Polling вместо WebSocket
- Сигналинг через HTTP polling каждые ~800 мс.
- Медленнее установка связи, лишняя нагрузка на сервер, хуже при многих зрителях.

### 4. Cache как шина сообщений
- Очереди в Laravel Cache (часто database/redis) — не для realtime.
- При нескольких PHP-воркерах возможны гонки и потеря сообщений.
- TTL 4 часа, нет гарантий доставки.

### 5. Нет записи и пересмотра
- Нет DVR, архива, HLS для iOS/Safari в фоне.
- Эфир только live, пока мастер в браузере.

### 6. Зависимость от браузера мастера
- Закрыл вкладку — эфир обрывается (если не нажал «Завершить»).
- Нет отдельного стримингового ПО (OBS и т.п.).

### 7. Безопасность и доступ
- Зритель без авторизации: любой со ссылкой может смотреть.
- Пароль мастера в БД в открытом виде (не хэш).
- Нет rate limiting на сигналинг.

### 8. Качество и адаптация
- Нет адаптивного битрейта (как у HLS).
- Качество зависит от uplink мастера и числа зрителей.
- Нет модерации, чата, синхронизации.

### 9. Ограничения WebRTC в браузере
- Нужен **HTTPS** (кроме localhost).
- На iOS/Safari возможны особенности с autoplay и фоном.
- Разные браузеры — разное поведение ICE.

---

## Когда этого достаточно

Подходит для **внутреннего пилота**: 1 мастер, 1–3 зрителя, оба в нормальной сети, короткие занятия.

## Когда нужно менять архитектуру

Для продакшена с десятками зрителей обычно ставят **медиасервер**:

- **LiveKit**, **Janus**, **mediasoup** — WebRTC SFU  
- **MediaMTX** — WHIP/WHEP или RTMP → HLS  
- **Cloudflare Stream**, **Mux**, **Agora** — managed-решения  

Тогда видео идёт: **мастер → сервер → зрители**, а не P2P с ноутбука мастера.

---

**Итог:** сейчас это лёгкий WebRTC P2P-прототип на Laravel для сигналинга. Для регулярных онлайн-занятий с надёжностью и масштабом понадобится медиасервер и, как минимум, TURN + WebSocket для сигналинга.

## User

Давай переделаем, чтобы видео шло через сервер и HLS, чтобы была возможность, во-первых, регулировать доступ, во-вторых, записывать записи, и в-третьих, оно нормально работало при 50 зрителях.

## Assistant

Переведём трансляцию на серверный HLS (MediaMTX): изучу текущую реализацию и спроектирую архитектуру с контролем доступа и записью.

### Tool New agent

### Tool Get Mcp Tools

```
{"server":"user-context7","toolName":"resolve-library-id","toolCallId":"tool_4ab702c7-fa98-47a2-aeee-4d0e647f98d"}
```

### Tool Mcp

```
{"name":"user-context7-resolve-library-id","args":{"libraryName":"MediaMTX","query":"MediaMTX WHIP ingest HLS output recording configuration paths"},"toolCallId":"tool_d757b1e2-4d9a-4450-a32b-4f0b3879796","providerIdentifier":"context7","toolName":"resolve-library-id","smartModeApprovalOnly":false,"skipApproval":false}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Http\\Controllers\\MasterCabinetController.php"}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Http\\Controllers\\OnlineStreamController.php"}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Models\\UserAbonement.php","limit":80}
```

### Tool Mcp

```
{"name":"user-context7-query-docs","args":{"libraryId":"/bluenviron/mediamtx","query":"WHIP publish browser HLS playback recording configuration yaml paths recordPath"},"toolCallId":"tool_0f0ade97-7c2e-4b98-9fbc-3e4a147f05f","providerIdentifier":"context7","toolName":"query-docs","smartModeApprovalOnly":false,"skipApproval":false}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Models\\LiveBroadcast.php"}
```

### Tool Ripgrep Raw Search

```
{}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Http\\Controllers\\Admin\\ScheduleAdminController.php","limit":120}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\config\\filesystems.php"}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\.env.example","limit":50}
```

### Tool Ripgrep Raw Search

```
{"pattern":"hall|Online","path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\admin\\schedules","caseInsensitive":false,"multiline":false,"toolCallId":"tool_a51d5063-1e14-43ec-9f3e-fa04fe50a36","offset":0}
```

### Tool Todo Write

## Assistant

Создаю серверную HLS-архитектуру на MediaMTX с контролем доступа и записью.

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Delete File

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Services\\WebRtcSignalingService.php","toolCallId":"tool_f9eda6a4-6bd2-4d77-a33f-30986112359"}
```

### Tool Edit File V2

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\admin\\schedules\\index.php","offset":60,"limit":40}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\admin\\schedules\\index.php","offset":560,"limit":40}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\admin\\schedules\\index.php","offset":285,"limit":30}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\admin\\schedules\\index.php","offset":475,"limit":30}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\admin\\schedules\\index.php","offset":735,"limit":30}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\admin\\schedules\\index.php","offset":375,"limit":40}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\admin\\schedules\\index.php","offset":500,"limit":30}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Http\\Controllers\\Admin\\ScheduleAdminController.php","offset":120,"limit":80}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Http\\Controllers\\Admin\\ScheduleAdminController.php","offset":198,"limit":60}
```

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Ripgrep Raw Search

```
{"pattern":"stream_access","path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\admin\\schedules\\index.php","caseInsensitive":false,"multiline":false,"toolCallId":"tool_fde9f911-6740-41d4-93bc-85a1031e613","offset":0}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\admin\\schedules\\index.php","offset":74,"limit":25}
```

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\admin\\schedules\\index.php","offset":76,"limit":15}
```

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\master\\cabinet\\broadcast.php","limit":35}
```

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Ripgrep Raw Search

```
{"pattern":"motion","path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\master\\cabinet\\broadcast.php","caseInsensitive":false,"multiline":false,"toolCallId":"tool_8c52f8ae-6a76-41e7-b74e-ee3597ecbbb","offset":0}
```

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Ripgrep Raw Search

```
{"pattern":"motion","path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views","caseInsensitive":false,"multiline":false,"toolCallId":"tool_3724c97a-0477-4f2c-a90e-c6bedbb4a3d","offset":0}
```

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Run Terminal Command V2

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\online_streams\\watch.php","limit":35}
```

### Tool Todo Write

## Assistant

Трансляция переведена с P2P WebRTC на **серверный HLS через MediaMTX**.

## Новая архитектура

```mermaid
flowchart LR
    M[Браузер мастера] -->|WHIP WebRTC| MTX[MediaMTX]
    MTX -->|HLS сегменты| L[Laravel proxy]
    L -->|подписанный URL| V[Зрители hls.js]
    MTX -->|fMP4 запись| DISK[storage/stream-recordings]
```

| Компонент | Роль |
|-----------|------|
| **MediaMTX** | Принимает поток от мастера (WHIP), отдаёт HLS, пишет запись на диск |
| **Laravel** | Контроль доступа, подписанные HLS-URL, прокси плейлиста/сегментов |
| **hls.js** | Воспроизведение у зрителей (50+ без нагрузки на ПК мастера) |

## Что сделано

1. **MediaMTX** — `docker-compose.yml`, `docker/mediamtx/mediamtx.yml`
2. **Миграция** — `stream_key`, `recording_*` в `live_broadcasts`; `stream_access` в `schedules`; таблица `stream_recordings`
3. **Доступ к просмотру** (`schedules.stream_access`):
   - `public` — всем
   - `authenticated` — только вошедшим (по умолчанию)
   - `abonement` — только с активным абонементом
4. **HLS-прокси** — `/stream/hls/{scheduleId}/{file}` с подписью и проверкой доступа
5. **Записи** — MediaMTX пишет fMP4; после остановки эфира `FinalizeStreamRecordingJob` сохраняет метаданные; просмотр через `/stream/recordings/{id}`
6. **Админка расписания** — поле «Доступ к трансляции» для онлайн-занятий
7. **Удалён** старый P2P-сигналинг (`WebRtcSignalingService`)

## Запуск

**1. Миграции:**
```bash
php artisan migrate
```

**2. MediaMTX:**
```bash
docker compose up -d
```

**3. `.env`:**
```env
STREAMING_WHIP_PUBLIC_URL=https://stream.inbi.ru   # HTTPS для камеры в браузере
STREAMING_HLS_INTERNAL_URL=http://127.0.0.1:8888 # только для Laravel
STREAMING_API_URL=http://127.0.0.1:9997
```

**4. Nginx** — проксировать `stream.inbi.ru` на порт `8889` (WHIP). Порт `8888` (HLS) **не открывать** наружу — только Laravel.

**5. Очередь** (для обработки записей):
```bash
php artisan queue:work
```

## Как это работает

- **Мастер:** `/master/cabinet/broadcast/{id}` → камера → WHIP → MediaMTX
- **Зритель:** `/online-streams/{id}` → проверка доступа → hls.js → Laravel-прокси → MediaMTX
- **Запись:** после «Завершить трансляцию» файл появляется в `storage/app/stream-recordings/`, доступен на странице занятия

## Важно

- WHIP **обязательно по HTTPS** (кроме localhost) — иначе браузер не даст камеру
- Для 50 зрителей HLS достаточно; при росте нагрузки можно поставить nginx-кэш перед HLS или CDN
- Записи сейчас автоматически `published=true`; при необходимости можно добавить модерацию в админке

Если нужно — могу подготовить пример конфига nginx для `stream.inbi.ru` под ваш OSPanel.

## User

Что там в докере? Не будет ли оно конфликтовать с текущим nginx на сервере?

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\docker-compose.yml"}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\docker\\mediamtx\\mediamtx.yml"}
```

## Assistant

В Docker сейчас **только MediaMTX** — один контейнер, без nginx и без Laravel.

## Что поднимается

| Порт | Назначение |
|------|------------|
| **8888** | HLS (внутренний, Laravel ходит сюда за плейлистом/сегментами) |
| **8889** | WHIP — приём потока от браузера мастера |
| **9997** | API MediaMTX (healthcheck/управление) |

Сайт по-прежнему обслуживает ваш текущий nginx (80/443) → PHP/Laravel.

## Конфликт с nginx?

**Нет**, если nginx слушает только 80 и 443. MediaMTX занимает другие порты.

Конфликт возможен только если на сервере уже что-то слушает **8888 / 8889 / 9997**.

## Как они должны жить рядом

```
Интернет
   │
   ├─ :443  nginx  → Laravel (test.inbi.ru, админка…)
   │                    │
   │                    └── HTTP → 127.0.0.1:8888  (HLS, только с сервера)
   │
   └─ :443  nginx  → proxy_pass http://127.0.0.1:8889  (WHIP для мастера)
                              │
                         Docker MediaMTX
```

- Laravel **сам** проксирует HLS зрителям — порт 8888 наружу открывать не нужно.
- Порт 8889 лучше **не светить в интернет напрямую**, а проксировать через nginx по HTTPS (иначе камера в браузере не заработает на проде).

## Если порты заняты

В `docker-compose.yml` можно сменить маппинг, например:

```yaml
ports:
  - "127.0.0.1:18888:8888"
  - "127.0.0.1:18889:8889"
  - "127.0.0.1:19997:9997"
```

и поправить `.env`:

```env
STREAMING_HLS_INTERNAL_URL=http://127.0.0.1:18888
STREAMING_WHIP_PUBLIC_URL=https://stream.inbi.ru   # через nginx
STREAMING_API_URL=http://127.0.0.1:19997
```

Привязка к `127.0.0.1` ещё и безопаснее: порты видны только с самой машины, не снаружи.

**Итог:** Docker не заменяет nginx и не конфликтует с ним по 80/443. Это отдельный медиасервер на других портах; nginx остаётся фронтом и при необходимости только проксирует WHIP.

## User

Ок. Скажи, что сейчас надо прописать в nginx, чтобы это заработало

## Assistant

Сверю, какие URL сейчас ожидает приложение, и дам готовый фрагмент для nginx.

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\config\\streaming.php"}
```

### Tool Ripgrep Raw Search

```
{"pattern":"STREAMING_|whip|8889|8888","path":"c:\\OSPanel\\home\\inbi.loc","glob":"*.{php,yml,env.example,md}","caseInsensitive":false,"multiline":false,"toolCallId":"call-a06a94f1-3e46-468e-b639-d7d31f461416-172\nfc_0cb4e356-885d-9b23-93c9-f10f8047979e_1","offset":0}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\master\\cabinet\\broadcast.php","offset":40,"limit":50}
```

## Assistant

Нужно проксировать **только WHIP** (порт 8889). HLS (8888) и API (9997) в nginx **не открывать** — Laravel ходит к ним сам по `127.0.0.1`.

## 1. `.env`

```env
STREAMING_WHIP_PUBLIC_URL=https://stream.inbi.ru
STREAMING_HLS_INTERNAL_URL=http://127.0.0.1:8888
STREAMING_API_URL=http://127.0.0.1:9997
```

Сайт мастера (`test.inbi.ru`) и WHIP (`stream.inbi.ru`) — разные домены, поэтому в nginx нужны CORS.

## 2. DNS

`stream.inbi.ru` → IP сервера (A-запись).

## 3. Nginx — отдельный vhost

```nginx
# /etc/nginx/sites-available/stream.inbi.ru

map $http_origin $stream_cors_origin {
    default "";
    "https://test.inbi.ru" $http_origin;
    "https://inbi.ru" $http_origin;
    "https://www.inbi.ru" $http_origin;
}

server {
    listen 80;
    server_name stream.inbi.ru;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name stream.inbi.ru;

    # свои пути к сертификатам
    ssl_certificate     /etc/letsencrypt/live/stream.inbi.ru/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/stream.inbi.ru/privkey.pem;

    # WHIP / WebRTC signaling → MediaMTX :8889
    location / {
        if ($request_method = OPTIONS) {
            add_header Access-Control-Allow-Origin $stream_cors_origin always;
            add_header Access-Control-Allow-Methods "GET, POST, OPTIONS, DELETE, PATCH" always;
            add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;
            add_header Access-Control-Expose-Headers "Location, Link" always;
            add_header Access-Control-Max-Age 86400 always;
            add_header Content-Length 0;
            add_header Content-Type text/plain;
            return 204;
        }

        proxy_pass http://127.0.0.1:8889;
        proxy_http_version 1.1;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # WHIP отдаёт Location сессии — переписываем на публичный HTTPS
        proxy_redirect http://127.0.0.1:8889/ https://stream.inbi.ru/;
        proxy_redirect http://localhost:8889/ https://stream.inbi.ru/;

        proxy_pass_header Location;
        proxy_hide_header Access-Control-Allow-Origin;

        add_header Access-Control-Allow-Origin $stream_cors_origin always;
        add_header Access-Control-Allow-Methods "GET, POST, OPTIONS, DELETE, PATCH" always;
        add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;
        add_header Access-Control-Expose-Headers "Location, Link" always;

        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
        client_max_body_size 0;
    }
}
```

Включить и проверить:

```bash
sudo ln -s /etc/nginx/sites-available/stream.inbi.ru /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
```

Сертификат, если через certbot:

```bash
sudo certbot --nginx -d stream.inbi.ru
```

## 4. UDP для медиа (иначе WHIP подключится, а картинки не будет)

Сигналинг идёт по HTTPS через nginx, а **видео/аудио WebRTC — по UDP**. MediaMTX по умолчанию слушает UDP `:8189`.

В `docker-compose.yml` добавьте порт:

```yaml
ports:
  - "8888:8888"
  - "8889:8889"
  - "8189:8189/udp"
  - "9997:9997"
```

В `docker/mediamtx/mediamtx.yml` укажите публичный адрес сервера:

```yaml
webrtcAddress: :8889
webrtcLocalUDPAddress: :8189
webrtcAdditionalHosts: [stream.inbi.ru]
# или публичный IP:
# webrtcAdditionalHosts: [1.2.3.4]
```

В файрволе откройте **UDP 8189**.

## 5. Что не трогать

| Порт | В nginx |
|------|---------|
| 80/443 текущего сайта | без изменений |
| 8888 HLS | не публиковать |
| 9997 API | не публиковать |

Зрители смотрят через Laravel: `/stream/hls/...` на `test.inbi.ru` — отдельный stream-vhost им не нужен.

## 6. Проверка

```bash
docker compose up -d
curl -I https://stream.inbi.ru/
```

Потом: мастер на `https://test.inbi.ru/master/cabinet` → «Начать трансляцию». Если в консоли CORS/WHIP 502 — смотрите, что MediaMTX слушает `127.0.0.1:8889` и origin сайта есть в `map`.

## User

root@vm736755:~# sudo certbot --nginx -d stream.inbi.ru
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Error while running nginx -c /etc/nginx/nginx.conf -t.

2026/07/22 18:25:00 [emerg] 2894280#2894280: cannot load certificate "/etc/letsencrypt/live/stream.inbi.ru/fullchain.pem": BIO_new_file() failed (SSL: error:80000002:system library::No such file or directory:calling fopen(/etc/letsencrypt/live/stream.inbi.ru/fullchain.pem, r) error:10000080:BIO routines::no such file)
nginx: configuration file /etc/nginx/nginx.conf test failed

The nginx plugin is not working; there may be problems with your existing configuration.
The error was: MisconfigurationError('Error while running nginx -c /etc/nginx/nginx.conf -t.\n\n2026/07/22 18:25:00 [emerg] 2894280#2894280: cannot load certificate "/etc/letsencrypt/live/stream.inbi.ru/fullchain.pem": BIO_new_file() failed (SSL: error:80000002:system library::No such file or directory:calling fopen(/etc/letsencrypt/live/stream.inbi.ru/fullchain.pem, r) error:10000080:BIO routines::no such file)\nnginx: configuration file /etc/nginx/nginx.conf test failed\n')

## Assistant

Это классическая «курица и яйцо»: в nginx уже прописаны пути к сертификату, которого ещё нет, поэтому `nginx -t` падает и certbot не может продолжить.

## Что сделать

### 1. Временно убрать HTTPS-блок для `stream.inbi.ru`

Оставьте **только HTTP** (порт 80):

```nginx
server {
    listen 80;
    server_name stream.inbi.ru;

    location / {
        proxy_pass http://127.0.0.1:8889;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
        client_max_body_size 0;
    }
}
```

Блок `listen 443 ssl` с `ssl_certificate ... stream.inbi.ru` **закомментируйте или удалите**.

### 2. Проверить и перезагрузить nginx

```bash
sudo nginx -t
sudo systemctl reload nginx
```

### 3. Выпустить сертификат

```bash
sudo certbot --nginx -d stream.inbi.ru
```

Certbot сам добавит HTTPS и редирект с 80 на 443.

### 4. После certbot — вернуть CORS и WHIP-настройки

В HTTPS-блок (который certbot создаст или обновит) добавьте `map` и заголовки из прошлой инструкции:

```nginx
map $http_origin $stream_cors_origin {
    default "";
    "https://test.inbi.ru" $http_origin;
    "https://inbi.ru" $http_origin;
}

# внутри server { listen 443 ssl; ... location / { ... } }
```

И снова:

```bash
sudo nginx -t && sudo systemctl reload nginx
```

---

## Если certbot снова ругается

Выпустите сертификат отдельно, без правки nginx:

```bash
sudo certbot certonly --nginx -d stream.inbi.ru
```

или через webroot, если сайт уже отдаётся:

```bash
sudo certbot certonly --webroot -w /var/www/html -d stream.inbi.ru
```

Потом вручную пропишите в HTTPS-блоке:

```nginx
ssl_certificate     /etc/letsencrypt/live/stream.inbi.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/stream.inbi.ru/privkey.pem;
```

---

## Перед certbot проверьте

```bash
dig +short stream.inbi.ru
curl -I http://stream.inbi.ru/
```

DNS должен указывать на этот сервер, а с 80-го порта nginx должен отвечать (не 502).

**Итог:** сначала nginx только на HTTP без ссылок на несуществующий `.pem`, потом certbot, потом полный HTTPS-конфиг с CORS.

## User

root@vm736755:/var/www/inbi# sudo certbot --nginx -d stream.inbi.ru
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for stream.inbi.ru
Encountered exception during recovery: certbot.errors.MisconfigurationError: nginx restart failed:
nginx: [emerg] bind() to 0.0.0.0:443 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:443 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:443 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:443 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:443 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
nginx: [emerg] still could not bind()
nginx restart failed:
nginx: [emerg] bind() to 0.0.0.0:443 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:443 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:443 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:443 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:443 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
nginx: [emerg] still could not bind()

Ask for help or search for solutions at https://community.letsencrypt.org. See the logfile /var/log/letsencrypt/letsencrypt.log or re-run Certbot with -v for more details.
root@vm736755:/var/www/inbi#

## Assistant

Сертификат, скорее всего, уже почти выпущен, а упал именно **рестарт nginx**: certbot пытается поднять второй nginx, пока старый всё ещё держит 80/443.

Обойдите плагин `--nginx` и выпустите сертификат отдельно.

## Вариант A (предпочтительный): webroot

Пока nginx уже слушает 80 для `stream.inbi.ru`:

```bash
# 1. Убедитесь, что HTTP-блок stream.inbi.ru отдаёт ACME-челлендж
# В location / или отдельным location:

# location ^~ /.well-known/acme-challenge/ {
#     root /var/www/certbot;
#     default_type text/plain;
# }

sudo mkdir -p /var/www/certbot
sudo nginx -t && sudo systemctl reload nginx

# 2. Только сертификат, без рестарта nginx
sudo certbot certonly --webroot -w /var/www/certbot -d stream.inbi.ru
```

В HTTP-конфиге `stream.inbi.ru` добавьте **выше** `location /`:

```nginx
location ^~ /.well-known/acme-challenge/ {
    root /var/www/certbot;
    default_type text/plain;
}

location / {
    proxy_pass http://127.0.0.1:8889;
    # ...
}
```

## Вариант B: остановить nginx на время

```bash
sudo systemctl stop nginx
sudo certbot certonly --standalone -d stream.inbi.ru
sudo systemctl start nginx
```

---

## После успешного выпуска

Проверьте, что файлы есть:

```bash
sudo ls -la /etc/letsencrypt/live/stream.inbi.ru/
```

Добавьте HTTPS-блок (или верните тот, что был) с этими путями:

```nginx
ssl_certificate     /etc/letsencrypt/live/stream.inbi.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/stream.inbi.ru/privkey.pem;
```

И:

```bash
sudo nginx -t
sudo systemctl reload nginx
```

Не `restart`, а именно `reload` — так не будет конфликта bind.

---

## Если после неудачного certbot nginx «битый»

```bash
sudo ss -tlnp | grep -E ':80|:443'
sudo systemctl status nginx
sudo nginx -t
```

Если висит несколько nginx или порт занят «зомби»:

```bash
sudo systemctl stop nginx
# при необходимости: sudo pkill nginx
sudo systemctl start nginx
```

---

## Почему так

`certbot --nginx` сам правит конфиг и делает restart. У вас порт уже занят рабочим nginx → второй процесс не биндится → ошибка 98.

`certbot certonly` только кладёт сертификат в `/etc/letsencrypt/live/...`, а конфиг nginx вы правите сами и делаете `reload`.

После `certonly` пришлите вывод `ls /etc/letsencrypt/live/stream.inbi.ru/` — подскажу точный HTTPS-блок под ваш текущий HTTP-конфиг.

## User

root@vm736755:/etc/nginx/sites-available# sudo systemctl stop nginx
root@vm736755:/etc/nginx/sites-available# service nginx stop
root@vm736755:/etc/nginx/sites-available# sudo certbot certonly --standalone -d stream.inbi.ru
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for stream.inbi.ru

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Could not bind TCP port 80 because it is already in use by another process on
this system (such as a web server). Please stop the program in question and then
try again.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(R)etry/(C)ancel: c
Could not bind TCP port 80 because it is already in use by another process on this system (such as a web server). Please stop the program in question and then try again.
Ask for help or search for solutions at https://community.letsencrypt.org. See the logfile /var/log/letsencrypt/letsencrypt.log or re-run Certbot with -v for more details.

## Assistant

Порт 80 всё ещё занят — это не обязательно nginx. Найдите процесс и либо остановите его, либо возьмите сертификат через webroot.

## 1. Кто держит 80/443

```bash
sudo ss -tlnp | grep -E ':80|:443'
# или
sudo lsof -i :80 -i :443
```

Часто это бывает:
- ещё один nginx (`nginx: master`)
- Apache (`apache2` / `httpd`)
- Caddy, Traefik, Docker-прокси
- другой экземпляр nginx не через systemd

## 2. Если это nginx / apache

```bash
# nginx
ps aux | grep nginx
sudo pkill -9 nginx

# apache
sudo systemctl stop apache2
# или
sudo systemctl stop httpd
```

Потом снова:

```bash
sudo ss -tlnp | grep ':80'
sudo certbot certonly --standalone -d stream.inbi.ru
sudo systemctl start nginx
```

## 3. Надёжнее: не трогать порт 80 — webroot

Если 80 должен оставаться у nginx (сайт живой), **не используйте standalone**.

Сначала поднимите nginx обратно:

```bash
sudo systemctl start nginx
```

Добавьте в HTTP-блок `stream.inbi.ru`:

```nginx
server {
    listen 80;
    server_name stream.inbi.ru;

    location ^~ /.well-known/acme-challenge/ {
        root /var/www/certbot;
        default_type text/plain;
    }

    location / {
        proxy_pass http://127.0.0.1:8889;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
```

```bash
sudo mkdir -p /var/www/certbot
sudo nginx -t && sudo systemctl reload nginx

# проверка снаружи
curl -I http://stream.inbi.ru/.well-known/acme-challenge/test

sudo certbot certonly --webroot -w /var/www/certbot -d stream.inbi.ru
```

После успеха:

```bash
sudo ls /etc/letsencrypt/live/stream.inbi.ru/
```

И уже тогда добавляйте `listen 443 ssl` с этими сертификатами + `reload`.

---

Пришлите вывод:

```bash
sudo ss -tlnp | grep -E ':80|:443'
```

по нему сразу скажу, что именно останавливать.

## User

root@vm736755:/var/www/inbi# docker compose up -d
unknown shorthand flag: 'd' in -d

Usage:  docker [OPTIONS] COMMAND [ARG...]

Run 'docker --help' for more information

## Assistant

На сервере, скорее всего, **нет Docker Compose v2** (плагин `docker compose`), поэтому флаг `-d` «ломается» на уровне CLI.

## Проверьте, что есть

```bash
docker --version
docker compose version          # v2-плагин
docker-compose --version        # старый отдельный бинарь
```

## Если есть старый `docker-compose` (с дефисом)

```bash
cd /var/www/inbi
sudo docker-compose up -d
```

## Если нет ни того, ни другого — поставьте Compose v2

Ubuntu/Debian:

```bash
sudo apt-get update
sudo apt-get install -y docker-compose-plugin

docker compose version
cd /var/www/inbi
sudo docker compose up -d
```

Или пакет со старым именем:

```bash
sudo apt-get install -y docker-compose
cd /var/www/inbi
sudo docker-compose up -d
```

## Проверка, что MediaMTX поднялся

```bash
sudo docker ps
curl -I http://127.0.0.1:8888/
curl -I http://127.0.0.1:8889/
```

---

**Важно:** `docker compose` (пробел) и `docker-compose` (дефис) — разные команды. У вас сработала первая без плагина — используйте вторую или поставьте `docker-compose-plugin`.

## User

Теперь трансляция включается, пишет "Эфир идёт — зрители смотрят через HLS".
Но зрители видят "Подключение к трансляции…" и больше ничего не показывает

## Assistant

Проверю цепочку HLS: от подписанного URL зрителя до проксирования плейлиста из MediaMTX.

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Services\\Streaming\\HlsProxyService.php"}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Http\\Controllers\\StreamHlsController.php"}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Services\\Streaming\\MediaMtxService.php"}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\online_streams\\watch.php"}
```

### Tool Mcp

```
{"name":"user-context7-query-docs","args":{"libraryId":"/bluenviron/mediamtx","query":"HLS playlist URL path format index.m3u8 segment names after WHIP publish"},"toolCallId":"call-ebdeef46-be53-4653-8a22-6faf0d658abf-178\nfc_922f4d2e-d2a9-94cc-b35a-1fa3f63271be_4","providerIdentifier":"context7","toolName":"query-docs","smartModeApprovalOnly":false,"skipApproval":false}
```

## Assistant

Нашёл несколько вероятных причин: не переписываются URI в `EXT-X-MAP` (fMP4 у MediaMTX), всегда тянется только `index.m3u8`, и подписанные URL могут строиться с неверным `APP_URL`. Исправляю.

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\routes\\web.php","offset":348,"limit":30}
```

### Tool Ripgrep Raw Search

```
{"pattern":"APP_URL|forceRootUrl|URL::","path":"c:\\OSPanel\\home\\inbi.loc\\config\\app.php","caseInsensitive":false,"multiline":false,"toolCallId":"call-1950057e-d7c3-4621-972a-6f9a6ed9442f-180\nfc_5dd73608-06ba-9511-8178-cbd71b2f8726_1","offset":0}
```

### Tool Mcp

```
{"name":"user-context7-query-docs","args":{"libraryId":"/bluenviron/mediamtx","query":"hlsVariant fMP4 EXT-X-MAP default HLS segment format"},"toolCallId":"call-1950057e-d7c3-4621-972a-6f9a6ed9442f-181\nfc_5dd73608-06ba-9511-8178-cbd71b2f8726_2","providerIdentifier":"context7","toolName":"query-docs","smartModeApprovalOnly":false,"skipApproval":false}
```

## Assistant

Перевожу HLS на query-параметр `file` (надёжнее для подписи) и корректно переписываю URI в плейлисте MediaMTX.

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Run Terminal Command V2

## Assistant

Исправил типичные причины зависания на «Подключение…».

## Что было не так

1. MediaMTX отдаёт **fMP4 HLS** с `EXT-X-MAP:URI="init.mp4"` — эти URI **не переписывались** в подписанные ссылки → сегменты шли без signature → 403.
2. Подписанный URL зависел от `APP_URL` (часто `localhost`) — плейлист мог открываться на неверный хост.
3. HLS у MediaMTX по умолчанию стартует **только при первом зрителе** → первые запросы давали 503.

## Что сделано

- Переписываются и обычные строки плейлиста, и `URI="..."` в тегах
- Подписанные URL **относительные** (`/stream/hls/...?file=...&signature=...`)
- `file` в query, а не в path
- В MediaMTX: `hlsAlwaysRemux: yes`, `hlsVariant: fmp4`
- На странице зрителя — диагностика HTTP-статуса плейлиста и ретраи

## Что сделать на сервере

```bash
cd /var/www/inbi

# обновить код, затем:
sudo docker-compose restart
# или: sudo docker compose restart

# очистить кэш конфига Laravel
php artisan config:clear
php artisan route:clear
```

Потом:
1. Мастер заново запускает эфир  
2. Зритель обновляет страницу (hard refresh)

Если снова «Подключение…» — на странице зрителя теперь должен появиться конкретный текст вроде `Плейлист: HTTP 503` или `403`. Пришлите его.

Быстрая проверка с сервера, пока мастер в эфире (подставьте `stream_key` из БД):

```bash
curl -sI "http://127.0.0.1:8888/live/STREAM_KEY/index.m3u8"
```

Должен быть `200`. Если `404` — поток до MediaMTX не доходит / неверный key.

## User

Теперь ошибка у зрителя 33:131  GET https://test.inbi.ru/stream/hls/33?expires=1784762693&file=index.m3u8&signature=f301a9ecf19ed5f6c70bf3ca5a733ffb8c7c4a73c9f66464238ffba62456575d 403 (Forbidden)

## Assistant

Похоже, подпись генерируется как относительный URL, а проверяется как абсолютный — из‑за этого Laravel отдаёт 403. Исправляю.

### Tool Edit File V2

### Tool Edit File V2

## Assistant

Для записей URL абсолютный — верну проверку подписи; для HLS оставлю relative.

### Tool Edit File V2

## Assistant

Причина: плейлист подписывался как **относительный** URL (`/stream/hls/...?signature=...`), а проверка шла как для **абсолютного** (`https://test.inbi.ru/...`) — подписи не совпадали → 403.

Исправление: `hasValidSignature(false)`.

Залейте правку на сервер, обновите страницу зрителя (лучше hard refresh) и попробуйте снова. Если снова 403 — в ответе будет текст; пришлите его (там уже будет понятно: подпись или доступ).

## User

Теперь у зрителя показывается "вы в эфире", но у видео черный экран, и в браузере:
33:96 HLS error {type: 'mediaError', details: 'fragGap', fatal: false, frag: e, error: Error: GAP tag found
    at si (https://cdn.jsdelivr.net/npm/hls.js@1.5.7/dist/hls.min.js:1:115860)…, …}
(anonymous) @ 33:96
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ base-stream-controller.ts:843
(anonymous) @ base-stream-controller.ts:789
Promise.catch
(anonymous) @ base-stream-controller.ts:789
(anonymous) @ base-stream-controller.ts:370
(anonymous) @ base-stream-controller.ts:346
(anonymous) @ stream-controller.ts:380
(anonymous) @ stream-controller.ts:356
(anonymous) @ stream-controller.ts:211
(anonymous) @ task-loop.ts:106
(anonymous) @ base-stream-controller.ts:545
(anonymous) @ base-stream-controller.ts:524
Promise.then
(anonymous) @ base-stream-controller.ts:478
(anonymous) @ stream-controller.ts:372
(anonymous) @ stream-controller.ts:356
(anonymous) @ stream-controller.ts:211
(anonymous) @ task-loop.ts:106
(anonymous) @ stream-controller.ts:696
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
set @ level-controller.ts:477
set @ level-controller.ts:629
set @ hls.ts:574
(anonymous) @ stream-controller.ts:143
(anonymous) @ hls.ts:434
(anonymous) @ hls.ts:433
(anonymous) @ level-controller.ts:376
(anonymous) @ level-controller.ts:200
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:429
onSuccess @ playlist-loader.ts:327
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:154
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ hls.ts:420
startHls @ 33:92
(anonymous) @ 33:142
Promise.then
(anonymous) @ 33:132
(anonymous) @ 33:145
33:96 HLS error {type: 'mediaError', details: 'fragGap', fatal: false, frag: e, error: Error: GAP tag found
    at si (https://cdn.jsdelivr.net/npm/hls.js@1.5.7/dist/hls.min.js:1:115860)…, …}
(anonymous) @ 33:96
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ base-stream-controller.ts:843
(anonymous) @ base-stream-controller.ts:789
Promise.catch
(anonymous) @ base-stream-controller.ts:789
(anonymous) @ base-stream-controller.ts:370
(anonymous) @ base-stream-controller.ts:346
(anonymous) @ stream-controller.ts:380
(anonymous) @ stream-controller.ts:356
(anonymous) @ stream-controller.ts:211
(anonymous) @ task-loop.ts:106
setTimeout
(anonymous) @ task-loop.ts:119
(anonymous) @ base-stream-controller.ts:1566
(anonymous) @ stream-controller.ts:900
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ base-stream-controller.ts:843
(anonymous) @ base-stream-controller.ts:789
Promise.catch
(anonymous) @ base-stream-controller.ts:789
(anonymous) @ base-stream-controller.ts:370
(anonymous) @ base-stream-controller.ts:346
(anonymous) @ stream-controller.ts:380
(anonymous) @ stream-controller.ts:356
(anonymous) @ stream-controller.ts:211
(anonymous) @ task-loop.ts:106
(anonymous) @ base-stream-controller.ts:545
(anonymous) @ base-stream-controller.ts:524
Promise.then
(anonymous) @ base-stream-controller.ts:478
(anonymous) @ stream-controller.ts:372
(anonymous) @ stream-controller.ts:356
(anonymous) @ stream-controller.ts:211
(anonymous) @ task-loop.ts:106
(anonymous) @ stream-controller.ts:696
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
set @ level-controller.ts:477
set @ level-controller.ts:629
set @ hls.ts:574
(anonymous) @ stream-controller.ts:143
(anonymous) @ hls.ts:434
(anonymous) @ hls.ts:433
(anonymous) @ level-controller.ts:376
(anonymous) @ level-controller.ts:200
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:429
onSuccess @ playlist-loader.ts:327
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:154
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ hls.ts:420
startHls @ 33:92
(anonymous) @ 33:142
Promise.then
(anonymous) @ 33:132
(anonymous) @ 33:145
33:96 HLS error {type: 'mediaError', details: 'fragGap', fatal: false, frag: e, error: Error: GAP tag found
    at si (https://cdn.jsdelivr.net/npm/hls.js@1.5.7/dist/hls.min.js:1:115860)…, …}
(anonymous) @ 33:96
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ base-stream-controller.ts:843
(anonymous) @ base-stream-controller.ts:789
Promise.catch
(anonymous) @ base-stream-controller.ts:789
(anonymous) @ base-stream-controller.ts:370
(anonymous) @ base-stream-controller.ts:346
(anonymous) @ stream-controller.ts:380
(anonymous) @ stream-controller.ts:356
(anonymous) @ stream-controller.ts:211
(anonymous) @ task-loop.ts:106
setTimeout
(anonymous) @ task-loop.ts:119
(anonymous) @ base-stream-controller.ts:294
seeking
(anonymous) @ stream-controller.ts:1028
(anonymous) @ base-stream-controller.ts:601
(anonymous) @ stream-controller.ts:884
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ buffer-controller.ts:605
(anonymous) @ buffer-controller.ts:1137
Promise.then
(anonymous) @ buffer-controller.ts:1135
(anonymous) @ buffer-controller.ts:619
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ base-stream-controller.ts:1758
(anonymous) @ base-stream-controller.ts:875
(anonymous) @ transmuxer-interface.ts:376
(anonymous) @ transmuxer-interface.ts:92
postMessage
Bn @ 5bc75f4f-adc9-4843-b9c8-053afbcfea23:1
(anonymous) @ 5bc75f4f-adc9-4843-b9c8-053afbcfea23:1
Worker.postMessage
(anonymous) @ transmuxer-interface.ts:296
(anonymous) @ base-stream-controller.ts:627
(anonymous) @ base-stream-controller.ts:394
Promise.then
(anonymous) @ base-stream-controller.ts:371
(anonymous) @ base-stream-controller.ts:346
(anonymous) @ stream-controller.ts:380
(anonymous) @ stream-controller.ts:356
(anonymous) @ stream-controller.ts:211
(anonymous) @ task-loop.ts:106
setTimeout
(anonymous) @ task-loop.ts:119
(anonymous) @ base-stream-controller.ts:1566
(anonymous) @ stream-controller.ts:900
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ base-stream-controller.ts:843
(anonymous) @ base-stream-controller.ts:789
Promise.catch
(anonymous) @ base-stream-controller.ts:789
(anonymous) @ base-stream-controller.ts:370
(anonymous) @ base-stream-controller.ts:346
(anonymous) @ stream-controller.ts:380
(anonymous) @ stream-controller.ts:356
(anonymous) @ stream-controller.ts:211
(anonymous) @ task-loop.ts:106
setTimeout
(anonymous) @ task-loop.ts:119
(anonymous) @ base-stream-controller.ts:1566
(anonymous) @ stream-controller.ts:900
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ base-stream-controller.ts:843
(anonymous) @ base-stream-controller.ts:789
Promise.catch
(anonymous) @ base-stream-controller.ts:789
(anonymous) @ base-stream-controller.ts:370
(anonymous) @ base-stream-controller.ts:346
(anonymous) @ stream-controller.ts:380
(anonymous) @ stream-controller.ts:356
(anonymous) @ stream-controller.ts:211
(anonymous) @ task-loop.ts:106
(anonymous) @ base-stream-controller.ts:545
(anonymous) @ base-stream-controller.ts:524
Promise.then
(anonymous) @ base-stream-controller.ts:478
(anonymous) @ stream-controller.ts:372
(anonymous) @ stream-controller.ts:356
(anonymous) @ stream-controller.ts:211
(anonymous) @ task-loop.ts:106
(anonymous) @ stream-controller.ts:696
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
set @ level-controller.ts:477
set @ level-controller.ts:629
set @ hls.ts:574
(anonymous) @ stream-controller.ts:143
(anonymous) @ hls.ts:434
(anonymous) @ hls.ts:433
(anonymous) @ level-controller.ts:376
(anonymous) @ level-controller.ts:200
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:429
onSuccess @ playlist-loader.ts:327
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:154
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ hls.ts:420
startHls @ 33:92
(anonymous) @ 33:142
Promise.then
(anonymous) @ 33:132
(anonymous) @ 33:145
33:96 HLS error {type: 'mediaError', details: 'fragGap', fatal: false, frag: e, error: Error: GAP tag found
    at si (https://cdn.jsdelivr.net/npm/hls.js@1.5.7/dist/hls.min.js:1:115860)…, …}
(anonymous) @ 33:96
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ base-stream-controller.ts:843
(anonymous) @ base-stream-controller.ts:789
Promise.catch
(anonymous) @ base-stream-controller.ts:789
(anonymous) @ base-stream-controller.ts:370
(anonymous) @ base-stream-controller.ts:346
(anonymous) @ stream-controller.ts:380
(anonymous) @ stream-controller.ts:356
(anonymous) @ stream-controller.ts:211
(anonymous) @ task-loop.ts:106
setTimeout
(anonymous) @ task-loop.ts:119
(anonymous) @ base-stream-controller.ts:1566
(anonymous) @ stream-controller.ts:900
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ base-stream-controller.ts:843
(anonymous) @ base-stream-controller.ts:789
Promise.catch
(anonymous) @ base-stream-controller.ts:789
(anonymous) @ base-stream-controller.ts:370
(anonymous) @ base-stream-controller.ts:346
(anonymous) @ stream-controller.ts:380
(anonymous) @ stream-controller.ts:356
(anonymous) @ stream-controller.ts:211
(anonymous) @ task-loop.ts:106
setTimeout
(anonymous) @ task-loop.ts:119
(anonymous) @ base-stream-controller.ts:294
seeking
(anonymous) @ stream-controller.ts:1028
(anonymous) @ base-stream-controller.ts:601
(anonymous) @ stream-controller.ts:884
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ buffer-controller.ts:605
(anonymous) @ buffer-controller.ts:1137
Promise.then
(anonymous) @ buffer-controller.ts:1135
(anonymous) @ buffer-controller.ts:619
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ base-stream-controller.ts:1758
(anonymous) @ base-stream-controller.ts:875
(anonymous) @ transmuxer-interface.ts:376
(anonymous) @ transmuxer-interface.ts:92
postMessage
Bn @ 5bc75f4f-adc9-4843-b9c8-053afbcfea23:1
(anonymous) @ 5bc75f4f-adc9-4843-b9c8-053afbcfea23:1
Worker.postMessage
(anonymous) @ transmuxer-interface.ts:296
(anonymous) @ base-stream-controller.ts:627
(anonymous) @ base-stream-controller.ts:394
Promise.then
(anonymous) @ base-stream-controller.ts:371
(anonymous) @ base-stream-controller.ts:346
(anonymous) @ stream-controller.ts:380
(anonymous) @ stream-controller.ts:356
(anonymous) @ stream-controller.ts:211
(anonymous) @ task-loop.ts:106
setTimeout
(anonymous) @ task-loop.ts:119
(anonymous) @ base-stream-controller.ts:1566
(anonymous) @ stream-controller.ts:900
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ base-stream-controller.ts:843
(anonymous) @ base-stream-controller.ts:789
Promise.catch
(anonymous) @ base-stream-controller.ts:789
(anonymous) @ base-stream-controller.ts:370
(anonymous) @ base-stream-controller.ts:346
(anonymous) @ stream-controller.ts:380
(anonymous) @ stream-controller.ts:356
(anonymous) @ stream-controller.ts:211
(anonymous) @ task-loop.ts:106
setTimeout
(anonymous) @ task-loop.ts:119
(anonymous) @ base-stream-controller.ts:1566
(anonymous) @ stream-controller.ts:900
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ base-stream-controller.ts:843
(anonymous) @ base-stream-controller.ts:789
Promise.catch
(anonymous) @ base-stream-controller.ts:789
(anonymous) @ base-stream-controller.ts:370
(anonymous) @ base-stream-controller.ts:346
(anonymous) @ stream-controller.ts:380
(anonymous) @ stream-controller.ts:356
(anonymous) @ stream-controller.ts:211
(anonymous) @ task-loop.ts:106
(anonymous) @ base-stream-controller.ts:545
(anonymous) @ base-stream-controller.ts:524
Promise.then
(anonymous) @ base-stream-controller.ts:478
(anonymous) @ stream-controller.ts:372
(anonymous) @ stream-controller.ts:356
(anonymous) @ stream-controller.ts:211
(anonymous) @ task-loop.ts:106
(anonymous) @ stream-controller.ts:696
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
set @ level-controller.ts:477
set @ level-controller.ts:629
set @ hls.ts:574
(anonymous) @ stream-controller.ts:143
(anonymous) @ hls.ts:434
(anonymous) @ hls.ts:433
(anonymous) @ level-controller.ts:376
(anonymous) @ level-controller.ts:200
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:429
onSuccess @ playlist-loader.ts:327
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:154
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ hls.ts:420
startHls @ 33:92
(anonymous) @ 33:142
Promise.then
(anonymous) @ 33:132
(anonymous) @ 33:145
xhr-loader.ts:166  GET https://test.inbi.ru/stream/hls/33?expires=1784762851&file=audio1_stream.m3u8&signature=b039966d9f35c6ddc100ea808238529a39e4f99c86b1979722cd8d6553d6be11&_HLS_msn=8&_HLS_part=4&_HLS_skip=YES 403 (Forbidden)
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
set @ level-controller.ts:477
set @ level-controller.ts:629
set @ hls.ts:574
(anonymous) @ stream-controller.ts:143
(anonymous) @ hls.ts:434
(anonymous) @ hls.ts:433
(anonymous) @ level-controller.ts:376
(anonymous) @ level-controller.ts:200
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:429
onSuccess @ playlist-loader.ts:327
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:154
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ hls.ts:420
startHls @ 33:92
(anonymous) @ 33:142
Promise.then
(anonymous) @ 33:132
(anonymous) @ 33:145
33:96 HLS error {type: 'networkError', details: 'levelLoadError', fatal: false, url: 'https://test.inbi.ru/stream/hls/33?expires=1784762…8d6553d6be11&_HLS_msn=8&_HLS_part=4&_HLS_skip=YES', loader: t, …}
(anonymous) @ 33:96
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
set @ level-controller.ts:477
set @ level-controller.ts:629
set @ hls.ts:574
(anonymous) @ stream-controller.ts:143
(anonymous) @ hls.ts:434
(anonymous) @ hls.ts:433
(anonymous) @ level-controller.ts:376
(anonymous) @ level-controller.ts:200
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:429
onSuccess @ playlist-loader.ts:327
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:154
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ hls.ts:420
startHls @ 33:92
(anonymous) @ 33:142
Promise.then
(anonymous) @ 33:132
(anonymous) @ 33:145
33:96 HLS error {type: 'mediaError', details: 'bufferStalledError', fatal: false, error: Error: Playback stalling at @6.942816 due to low buffer ({"len":0.057184000000000346,"start":6,"end…, buffer: 0.057184000000000346, …}
(anonymous) @ 33:96
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ gap-controller.ts:234
(anonymous) @ gap-controller.ts:157
(anonymous) @ stream-controller.ts:946
(anonymous) @ stream-controller.ts:218
(anonymous) @ stream-controller.ts:213
(anonymous) @ task-loop.ts:106
setInterval
(anonymous) @ task-loop.ts:68
(anonymous) @ stream-controller.ts:127
(anonymous) @ hls.ts:434
(anonymous) @ hls.ts:433
(anonymous) @ level-controller.ts:376
(anonymous) @ level-controller.ts:200
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:429
onSuccess @ playlist-loader.ts:327
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:154
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ hls.ts:420
startHls @ 33:92
(anonymous) @ 33:142
Promise.then
(anonymous) @ 33:132
(anonymous) @ 33:145
xhr-loader.ts:166  GET https://test.inbi.ru/stream/hls/33?expires=1784762851&file=audio1_stream.m3u8&signature=b039966d9f35c6ddc100ea808238529a39e4f99c86b1979722cd8d6553d6be11&_HLS_msn=10&_HLS_part=4&_HLS_skip=YES 403 (Forbidden)
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
set @ level-controller.ts:477
set @ level-controller.ts:629
set @ hls.ts:574
(anonymous) @ stream-controller.ts:143
(anonymous) @ hls.ts:434
(anonymous) @ hls.ts:433
(anonymous) @ level-controller.ts:376
(anonymous) @ level-controller.ts:200
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:429
onSuccess @ playlist-loader.ts:327
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:154
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ hls.ts:420
startHls @ 33:92
(anonymous) @ 33:142
Promise.then
(anonymous) @ 33:132
(anonymous) @ 33:145
33:96 HLS error {type: 'networkError', details: 'levelLoadError', fatal: false, url: 'https://test.inbi.ru/stream/hls/33?expires=1784762…d6553d6be11&_HLS_msn=10&_HLS_part=4&_HLS_skip=YES', loader: t, …}
(anonymous) @ 33:96
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
set @ level-controller.ts:477
set @ level-controller.ts:629
set @ hls.ts:574
(anonymous) @ stream-controller.ts:143
(anonymous) @ hls.ts:434
(anonymous) @ hls.ts:433
(anonymous) @ level-controller.ts:376
(anonymous) @ level-controller.ts:200
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:429
onSuccess @ playlist-loader.ts:327
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:154
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ hls.ts:420
startHls @ 33:92
(anonymous) @ 33:142
Promise.then
(anonymous) @ 33:132
(anonymous) @ 33:145
xhr-loader.ts:166  GET https://test.inbi.ru/stream/hls/33?expires=1784762851&file=audio1_stream.m3u8&signature=b039966d9f35c6ddc100ea808238529a39e4f99c86b1979722cd8d6553d6be11&_HLS_msn=12&_HLS_part=4&_HLS_skip=YES 403 (Forbidden)
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
set @ level-controller.ts:477
set @ level-controller.ts:629
set @ hls.ts:574
(anonymous) @ stream-controller.ts:143
(anonymous) @ hls.ts:434
(anonymous) @ hls.ts:433
(anonymous) @ level-controller.ts:376
(anonymous) @ level-controller.ts:200
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:429
onSuccess @ playlist-loader.ts:327
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:154
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ hls.ts:420
startHls @ 33:92
(anonymous) @ 33:142
Promise.then
(anonymous) @ 33:132
(anonymous) @ 33:145
33:96 HLS error {type: 'networkError', details: 'levelLoadError', fatal: false, url: 'https://test.inbi.ru/stream/hls/33?expires=1784762…d6553d6be11&_HLS_msn=12&_HLS_part=4&_HLS_skip=YES', loader: t, …}
(anonymous) @ 33:96
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
set @ level-controller.ts:477
set @ level-controller.ts:629
set @ hls.ts:574
(anonymous) @ stream-controller.ts:143
(anonymous) @ hls.ts:434
(anonymous) @ hls.ts:433
(anonymous) @ level-controller.ts:376
(anonymous) @ level-controller.ts:200
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:429
onSuccess @ playlist-loader.ts:327
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:154
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ hls.ts:420
startHls @ 33:92
(anonymous) @ 33:142
Promise.then
(anonymous) @ 33:132
(anonymous) @ 33:145
xhr-loader.ts:166  GET https://test.inbi.ru/stream/hls/33?expires=1784762851&file=audio1_stream.m3u8&signature=b039966d9f35c6ddc100ea808238529a39e4f99c86b1979722cd8d6553d6be11&_HLS_msn=14&_HLS_part=1&_HLS_skip=YES 403 (Forbidden)
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:228
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
set @ level-controller.ts:477
set @ level-controller.ts:629
set @ hls.ts:574
(anonymous) @ stream-controller.ts:143
(anonymous) @ hls.ts:434
(anonymous) @ hls.ts:433
(anonymous) @ level-controller.ts:376
(anonymous) @ level-controller.ts:200
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:429
onSuccess @ playlist-loader.ts:327
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:154
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ hls.ts:420
startHls @ 33:92
(anonymous) @ 33:142
Promise.then
(anonymous) @ 33:132
(anonymous) @ 33:145
33:96 HLS error {type: 'networkError', details: 'levelLoadError', fatal: false, url: 'https://test.inbi.ru/stream/hls/33?expires=1784762…d6553d6be11&_HLS_msn=14&_HLS_part=1&_HLS_skip=YES', loader: t, …}
(anonymous) @ 33:96
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:228
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
set @ level-controller.ts:477
set @ level-controller.ts:629
set @ hls.ts:574
(anonymous) @ stream-controller.ts:143
(anonymous) @ hls.ts:434
(anonymous) @ hls.ts:433
(anonymous) @ level-controller.ts:376
(anonymous) @ level-controller.ts:200
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:429
onSuccess @ playlist-loader.ts:327
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:154
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ hls.ts:420
startHls @ 33:92
(anonymous) @ 33:142
Promise.then
(anonymous) @ 33:132
(anonymous) @ 33:145
xhr-loader.ts:166  GET https://test.inbi.ru/stream/hls/33?expires=1784762851&file=audio1_stream.m3u8&signature=b039966d9f35c6ddc100ea808238529a39e4f99c86b1979722cd8d6553d6be11&_HLS_msn=15&_HLS_part=2&_HLS_skip=YES 403 (Forbidden)
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:228
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:228
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
set @ level-controller.ts:477
set @ level-controller.ts:629
set @ hls.ts:574
(anonymous) @ stream-controller.ts:143
(anonymous) @ hls.ts:434
(anonymous) @ hls.ts:433
(anonymous) @ level-controller.ts:376
(anonymous) @ level-controller.ts:200
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:429
onSuccess @ playlist-loader.ts:327
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:154
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ hls.ts:420
startHls @ 33:92
(anonymous) @ 33:142
Promise.then
(anonymous) @ 33:132
(anonymous) @ 33:145
33:96 HLS error {type: 'networkError', details: 'levelLoadError', fatal: false, url: 'https://test.inbi.ru/stream/hls/33?expires=1784762…d6553d6be11&_HLS_msn=15&_HLS_part=2&_HLS_skip=YES', loader: t, …}
(anonymous) @ 33:96
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:228
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:228
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
set @ level-controller.ts:477
set @ level-controller.ts:629
set @ hls.ts:574
(anonymous) @ stream-controller.ts:143
(anonymous) @ hls.ts:434
(anonymous) @ hls.ts:433
(anonymous) @ level-controller.ts:376
(anonymous) @ level-controller.ts:200
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:429
onSuccess @ playlist-loader.ts:327
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:154
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ hls.ts:420
startHls @ 33:92
(anonymous) @ 33:142
Promise.then
(anonymous) @ 33:132
(anonymous) @ 33:145
xhr-loader.ts:166  GET https://test.inbi.ru/stream/hls/33?expires=1784762851&file=audio1_stream.m3u8&signature=b039966d9f35c6ddc100ea808238529a39e4f99c86b1979722cd8d6553d6be11&_HLS_msn=16&_HLS_part=4&_HLS_skip=YES 403 (Forbidden)
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:228
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:228
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:228
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
set @ level-controller.ts:477
set @ level-controller.ts:629
set @ hls.ts:574
(anonymous) @ stream-controller.ts:143
(anonymous) @ hls.ts:434
(anonymous) @ hls.ts:433
(anonymous) @ level-controller.ts:376
(anonymous) @ level-controller.ts:200
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:429
onSuccess @ playlist-loader.ts:327
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:154
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ hls.ts:420
startHls @ 33:92
(anonymous) @ 33:142
Promise.then
(anonymous) @ 33:132
(anonymous) @ 33:145
33:96 HLS error {type: 'networkError', details: 'levelLoadError', fatal: false, url: 'https://test.inbi.ru/stream/hls/33?expires=1784762…d6553d6be11&_HLS_msn=16&_HLS_part=4&_HLS_skip=YES', loader: t, …}
(anonymous) @ 33:96
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:228
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:228
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:228
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
set @ level-controller.ts:477
set @ level-controller.ts:629
set @ hls.ts:574
(anonymous) @ stream-controller.ts:143
(anonymous) @ hls.ts:434
(anonymous) @ hls.ts:433
(anonymous) @ level-controller.ts:376
(anonymous) @ level-controller.ts:200
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:429
onSuccess @ playlist-loader.ts:327
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:154
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ hls.ts:420
startHls @ 33:92
(anonymous) @ 33:142
Promise.then
(anonymous) @ 33:132
(anonymous) @ 33:145
xhr-loader.ts:166  GET https://test.inbi.ru/stream/hls/33?expires=1784762851&file=audio1_stream.m3u8&signature=b039966d9f35c6ddc100ea808238529a39e4f99c86b1979722cd8d6553d6be11&_HLS_msn=18&_HLS_part=1&_HLS_skip=YES 403 (Forbidden)
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:228
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:228
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:228
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:228
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:337
setTimeout
(anonymous) @ base-playlist-controller.ts:337
(anonymous) @ level-controller.ts:529
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onError @ playlist-loader.ts:331
(anonymous) @ xhr-loader.ts:253
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
set @ level-controller.ts:477
set @ level-controller.ts:629
set @ hls.ts:574
(anonymous) @ stream-controller.ts:143
(anonymous) @ hls.ts:434
(anonymous) @ hls.ts:433
(anonymous) @ level-controller.ts:376
(anonymous) @ level-controller.ts:200
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:429
onSuccess @ playlist-loader.ts:327
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:154
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ hls.ts:420
startHls @ 33:92
(anonymous) @ 33:142
Promise.then
(anonymous) @ 33:132
(anonymous) @ 33:145
33:96 HLS error {type: 'networkError', details: 'levelLoadError', fatal: false, url: 'https://test.inbi.ru/stream/hls/33?expires=1784762…d6553d6be11&_HLS_msn=18&_HLS_part=1&_HLS_skip=YES', loader: t, …}

## Assistant

Hls.js при обновлении плейлиста добавляет `_HLS_msn` / `_HLS_part` / `_HLS_skip` — из‑за них ломается подпись Laravel (403). Плюс в потоке ещё LL-HLS (`fragGap`). Исправляю оба момента.

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Http\\Controllers\\StreamHlsController.php"}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\docker\\mediamtx\\mediamtx.yml"}
```

### Tool Get Mcp Tools

```
{"server":"user-context7","toolName":"query-docs","toolCallId":"call-567fc535-fb8d-4dba-87f5-e059ed215914-194\nfc_6d00e56c-d86c-9995-8971-df27373af69e_2"}
```

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Mcp

```
{"name":"user-context7-query-docs","args":{"libraryId":"/websites/laravel_12_x","query":"hasValidSignatureWhileIgnoring relative absolute signed URL"},"toolCallId":"call-533b1dde-0204-4ab0-966c-4fbb9437f89c-199\nfc_c7c183a8-c30d-95c0-ab20-12ffb813e108_0","providerIdentifier":"context7","toolName":"query-docs","smartModeApprovalOnly":false,"skipApproval":false}
```

### Tool Ripgrep Raw Search

```
{}
```

## Assistant

Две причины чёрного экрана:

1. **hls.js** при обновлении плейлиста дописывает `_HLS_msn`, `_HLS_part`, `_HLS_skip` → подпись Laravel ломается → **403** на `audio1_stream.m3u8`
2. MediaMTX отдавал **LL-HLS** (`fragGap`, отдельные audio/video playlist)

## Что исправлено

- Подпись игнорирует `_HLS_*` параметры
- Эти параметры пробрасываются в MediaMTX при запросе плейлиста
- В MediaMTX включён **`hlsVariant: mpegts`** (совместимее с hls.js, без LL-HLS)

## На сервере

```bash
cd /var/www/inbi

# обновить код + конфиг mediamtx.yml
sudo docker-compose down
sudo docker-compose up -d
# или: sudo docker compose down && sudo docker compose up -d

php artisan config:clear
```

Затем:
1. Мастер **полностью останавливает** эфир и запускает заново  
2. Зритель делает hard refresh  

В Network у зрителя сегменты должны быть вида `*.ts` (не `audio1_stream.m3u8` с `_HLS_*`). Если `audio1_*` всё ещё есть — MediaMTX не подхватил новый `mediamtx.yml` (проверьте volume и restart).

## User

Теперь ошибка 


33:136 
 GET https://test.inbi.ru/stream/hls/33?expires=1784763193&file=index.m3u8&signature=e839995… 503 (Service Unavailable)

## Assistant

Скорее всего `mpegts` не стыкуется с WebRTC (Opus/VP8) — плейлист с MediaMTX не отдаётся. Вернём `fmp4` и усилим диагностику 503.

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

## Assistant

**503** значит: Laravel не смог получить плейлист у MediaMTX. После смены на `mpegts` это ожидаемо — WebRTC (Opus) часто **не собирается** в MPEG-TS без перекодирования, MediaMTX отдаёт 404, Laravel → 503.

Вернул **`hlsVariant: fmp4`** (без LL-HLS). Игнор `_HLS_*` в подписи оставил.

## На сервере

```bash
cd /var/www/inbi
# залить обновлённый mediamtx.yml и код

sudo docker-compose down
sudo docker-compose up -d

php artisan config:clear
```

Затем **обязательно**:
1. Мастер нажимает «Завершить трансляцию»
2. Снова «Начать трансляцию» (после рестарта Docker старый WHIP-сеанс мёртв)
3. Зритель обновляет страницу

## Проверка

Пока мастер в эфире:

```bash
# stream_key из БД:
# SELECT id, stream_key, status FROM live_broadcasts ORDER BY id DESC LIMIT 1;

curl -sI "http://127.0.0.1:8888/live/STREAM_KEY/index.m3u8"
sudo docker ps
sudo docker logs inbi-mediamtx --tail 50
```

Нужен **HTTP 200**. Если 404 — поток не опубликован в MediaMTX. Если `Connection refused` — контейнер не слушает 8888.

В ответе 503 теперь будет текст с причиной — пришлите его, если снова не заработает.

## User

все еще не отдает. Вот ответ:
root@vm736755:/var/www/inbicurl -sI "http://127.0.0.1:8888/live/STREAM_KEY/index.m3u8"8"
sudo docker ps
sudo docker logs inbi-mediamtx --tail 50
HTTP/1.1 404 Not Found
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Content-Type: text/plain
Server: mediamtx
Date: Wed, 22 Jul 2026 19:44:02 GMT
Content-Length: 18

CONTAINER ID   IMAGE                        COMMAND       CREATED         STATUS         PORTS                                                                                                          NAMES
53d530c6f878   bluenviron/mediamtx:1.11.3   "/mediamtx"   5 minutes ago   Up 5 minutes   0.0.0.0:8888-8889->8888-8889/tcp, [::]:8888-8889->8888-8889/tcp, 0.0.0.0:9997->9997/tcp, [::]:9997->9997/tcp   inbi-mediamtx
2026/07/22 19:38:59 INF MediaMTX v1.11.3
2026/07/22 19:38:59 INF configuration loaded from /mediamtx.yml
2026/07/22 19:38:59 INF [RTSP] listener opened on :8554 (TCP), :8000 (UDP/RTP), :8001 (UDP/RTCP)
2026/07/22 19:38:59 INF [RTMP] listener opened on :1935
2026/07/22 19:38:59 INF [HLS] listener opened on :8888
2026/07/22 19:38:59 INF [WebRTC] listener opened on :8889 (HTTP), :8189 (ICE/UDP)
2026/07/22 19:38:59 INF [SRT] listener opened on :8890 (UDP)
2026/07/22 19:38:59 INF [API] listener opened on :9997
2026/07/22 19:41:42 INF [WebRTC] [session 41bb87f6] created by 172.18.0.1:47472
2026/07/22 19:41:52 INF [WebRTC] [session 41bb87f6] closed: deadline exceeded while waiting connection

## Assistant

В логах видно главное: WHIP-сессия создаётся, но обрывается — `deadline exceeded while waiting connection`. Медиа по UDP не устанавливается. В `docker ps` нет порта `8189/udp`.

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\docker-compose.yml"}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\docker\\mediamtx\\mediamtx.yml"}
```

### Tool Mcp

```
{"name":"user-context7-query-docs","args":{"libraryId":"/bluenviron/mediamtx","query":"WebRTC ICE UDP mux webrtcAdditionalHosts NAT docker deadline exceeded while waiting connection"},"toolCallId":"call-632b8781-ea65-4928-ade9-86a52e79f6f1-206\nfc_8e9c5a54-e9cb-92d6-8ff2-cdd884c3cf09_2","providerIdentifier":"context7","toolName":"query-docs","smartModeApprovalOnly":false,"skipApproval":false}
```

## Assistant

WHIP-сигналинг проходит, но ICE/UDP не устанавливается — в Docker не проброшен порт `8189`. Исправляю.

### Tool Edit File V2

### Tool Edit File V2

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\master\\cabinet\\broadcast.php"}
```

### Tool Edit File V2

## Assistant

Проблема не в HLS-прокси. В логах MediaMTX:

```text
closed: deadline exceeded while waiting connection
```

WHIP (сигналинг по HTTPS) проходит, а **медиапоток по WebRTC/ICE не устанавливается**. В `docker ps` был только TCP `8888-8889`, **без UDP 8189** — браузер не может отдать видео на сервер, путь в MediaMTX не появляется → HLS `404` → у зрителя `503`.

`curl .../live/STREAM_KEY/...` тоже был с плейсхолдером, не с реальным ключом — но это вторично.

## Что изменено

1. Docker → **`network_mode: host`** (без Docker NAT для ICE)
2. ICE: UDP + TCP **8189**, STUN, `webrtcAdditionalHosts`
3. У мастера статус «Эфир идёт» только после **реального ICE connected**

## На сервере

```bash
cd /var/www/inbi
# залить docker-compose.yml и mediamtx.yml

# если в mediamtx.yml только домен — лучше добавить ПУБЛИЧНЫЙ IP:
# webrtcAdditionalHosts: [stream.inbi.ru, 1.2.3.4]

sudo docker-compose down
sudo docker-compose up -d

# файрвол — обязательно
sudo ufw allow 8189/udp
sudo ufw allow 8189/tcp
# 8889 уже должен быть доступен через nginx; 8888 наружу не нужен
```

Проверка:

```bash
sudo ss -ulnp | grep 8189
sudo ss -tlnp | grep -E '8888|8889|8189'
sudo docker logs inbi-mediamtx --tail 20
```

## Тест эфира

1. Мастер заново начинает трансляцию  
2. В статусе должно быть «Ожидание ICE…», затем «Эфир идёт»  
3. В логах MediaMTX должно появиться что-то вроде `peer connection established` / `path created`, **не** `deadline exceeded`  
4. Затем:

```bash
# реальный ключ:
mysql ... -e "SELECT id, stream_key, status FROM live_broadcasts ORDER BY id DESC LIMIT 1;"

curl -sI "http://127.0.0.1:8888/live/РЕАЛЬНЫЙ_КЛЮЧ/index.m3u8"
```

Ожидается **200**.

Если ICE снова timeout — пришлите публичный IP сервера и кусок SDP answer (или лог MediaMTX после новой попытки). Тогда пропишем IP явно в `webrtcAdditionalHosts` или добавим TURN.

## User

Теперь никаких ошибок нет, пишет "вы в эфире". Но экран видео черный.
root@vm736755:/var/www/inbi# sudo docker logs inbi-mediamtx --tail 20
2026/07/22 19:46:13 INF MediaMTX v1.11.3
2026/07/22 19:46:13 INF configuration loaded from /mediamtx.yml
2026/07/22 19:46:13 INF [RTSP] listener opened on :8554 (TCP), :8000 (UDP/RTP), :8001 (UDP/RTCP)
2026/07/22 19:46:13 INF [RTMP] listener opened on :1935
2026/07/22 19:46:13 INF [HLS] listener opened on :8888
2026/07/22 19:46:13 INF [WebRTC] listener opened on :8889 (HTTP), :8189 (ICE/UDP), :8189 (ICE/TCP)
2026/07/22 19:46:13 INF [SRT] listener opened on :8890 (UDP)
2026/07/22 19:46:13 INF [API] listener opened on :9997
2026/07/22 19:47:40 INF [WebRTC] [session bc09935c] created by 127.0.0.1:46456
2026/07/22 19:47:47 INF [WebRTC] [session bc09935c] peer connection established, local candidate: host/udp/127.0.0.1/8189, remote candidate: prflx/udp/178.157.135.193/18222
2026/07/22 19:47:47 WAR [path live/dq8q90n6o031vcsua0jk4k2kpna66u0t] [recorder] skipping track 2 (VP8)
2026/07/22 19:47:47 INF [path live/dq8q90n6o031vcsua0jk4k2kpna66u0t] [recorder] recording 1 track (Opus)
2026/07/22 19:47:47 INF [WebRTC] [session bc09935c] is publishing to path 'live/dq8q90n6o031vcsua0jk4k2kpna66u0t', 2 tracks (Opus, VP8)
2026/07/22 19:47:47 INF [HLS] [muxer live/dq8q90n6o031vcsua0jk4k2kpna66u0t] created automatically
2026/07/22 19:47:47 WAR [HLS] [muxer live/dq8q90n6o031vcsua0jk4k2kpna66u0t] skipping track 2 (VP8)
2026/07/22 19:47:47 INF [HLS] [muxer live/dq8q90n6o031vcsua0jk4k2kpna66u0t] is converting into HLS, 1 track (Opus)

## Assistant

В логах ясно: MediaMTX пропускает VP8 и в HLS попадает только Opus (звук без картинки). Браузер мастера шлёт VP8 — для HLS нужен H.264. Принудительно выставлю H.264 на стороне мастера.

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\master\\cabinet\\broadcast.php"}
```

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

## Assistant

Причина чёрного экрана в логах:

```text
skipping track 2 (VP8)
is converting into HLS, 1 track (Opus)
```

Браузер мастера слал **VP8**. MediaMTX в HLS **не кладёт VP8** — только Opus. Зритель получает поток без видео.

## Что сделано

На стороне мастера принудительно:
- предпочтение **H.264** через `setCodecPreferences`
- вырезание VP8/VP9 из SDP
- проверка исходящего кодека после ICE

## Что сделать

1. Залить обновлённый `broadcast.php`
2. Мастер: **завершить** эфир и начать заново (лучше в **Chrome/Edge**)
3. В логах должно быть:

```bash
sudo docker logs inbi-mediamtx --tail 30
```

Ожидается примерно:
```text
is publishing ... (H264, Opus)
is converting into HLS, 2 tracks (H264, Opus)
```

Не `VP8` и не `1 track (Opus)`.

4. Зритель — hard refresh

Если снова будет `VP8` — напишите, какой браузер у мастера (Firefox часто без нормального H.264).

## User

Теперь все работает, но задержка почти 30 секунд. И в консоли браузера зрителя:
33:101 HLS error {type: 'mediaError', details: 'bufferStalledError', fatal: false, error: Error: Playback stalling at @19.297073 due to low buffer ({"len":0,"start":19.297073,"end":19.29707…, buffer: 0, …}
(anonymous) @ 33:101
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ gap-controller.ts:234
(anonymous) @ gap-controller.ts:157
(anonymous) @ stream-controller.ts:946
(anonymous) @ stream-controller.ts:218
(anonymous) @ stream-controller.ts:213
(anonymous) @ task-loop.ts:106
setInterval
(anonymous) @ task-loop.ts:68
(anonymous) @ stream-controller.ts:127
(anonymous) @ hls.ts:434
(anonymous) @ hls.ts:433
(anonymous) @ level-controller.ts:376
(anonymous) @ level-controller.ts:200
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:429
onSuccess @ playlist-loader.ts:327
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:154
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ hls.ts:420
startHls @ 33:97
(anonymous) @ 33:147
Promise.then
(anonymous) @ 33:137
(anonymous) @ 33:150
33:101 HLS error {type: 'mediaError', details: 'bufferStalledError', fatal: false, error: Error: Playback stalling at @23.191927 due to low buffer ({"len":0,"start":23.191927,"end":23.19192…, buffer: 0, …}
(anonymous) @ 33:101
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ gap-controller.ts:234
(anonymous) @ gap-controller.ts:157
(anonymous) @ stream-controller.ts:946
(anonymous) @ stream-controller.ts:218
(anonymous) @ stream-controller.ts:213
(anonymous) @ task-loop.ts:106
setInterval
(anonymous) @ task-loop.ts:68
(anonymous) @ stream-controller.ts:127
(anonymous) @ hls.ts:434
(anonymous) @ hls.ts:433
(anonymous) @ level-controller.ts:376
(anonymous) @ level-controller.ts:200
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:429
onSuccess @ playlist-loader.ts:327
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:154
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ hls.ts:420
startHls @ 33:97
(anonymous) @ 33:147
Promise.then
(anonymous) @ 33:137
(anonymous) @ 33:150
33:101 HLS error {type: 'mediaError', details: 'bufferNudgeOnStall', error: Error: Nudging 'currentTime' from 23.191927 to 23.291927
    at e._tryNudgeBuffer (https://cdn.jsde…, fatal: false, errorAction: {…}}
(anonymous) @ 33:101
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ gap-controller.ts:353
(anonymous) @ gap-controller.ts:214
(anonymous) @ gap-controller.ts:168
(anonymous) @ stream-controller.ts:946
(anonymous) @ stream-controller.ts:218
(anonymous) @ stream-controller.ts:213
(anonymous) @ task-loop.ts:106
(anonymous) @ stream-controller.ts:696
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ level-controller.ts:571
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:683
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:166
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ level-controller.ts:610
set @ level-controller.ts:477
set @ level-controller.ts:629
set @ hls.ts:574
(anonymous) @ stream-controller.ts:143
(anonymous) @ hls.ts:434
(anonymous) @ hls.ts:433
(anonymous) @ level-controller.ts:376
(anonymous) @ level-controller.ts:200
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:429
onSuccess @ playlist-loader.ts:327
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:154
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ hls.ts:420
startHls @ 33:97
(anonymous) @ 33:147
Promise.then
(anonymous) @ 33:137
(anonymous) @ 33:150
33:101 HLS error {type: 'networkError', details: 'audioTrackLoadTimeOut', fatal: false, url: 'https://test.inbi.ru/stream/hls/33?expires=1784764…d1b7b9b148a40cf4bb0395488dfd8afd2c4d2465e7e7e6aee', loader: t, …}
(anonymous) @ 33:101
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:612
onTimeout @ playlist-loader.ts:340
(anonymous) @ xhr-loader.ts:275
setTimeout
(anonymous) @ xhr-loader.ts:162
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:287
setTimeout
(anonymous) @ base-playlist-controller.ts:286
(anonymous) @ audio-track-controller.ts:105
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:693
(anonymous) @ playlist-loader.ts:505
onSuccess @ playlist-loader.ts:319
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:182
(anonymous) @ index.js:182
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ audio-track-controller.ts:422
(anonymous) @ base-playlist-controller.ts:47
(anonymous) @ hls.ts:434
(anonymous) @ hls.ts:433
(anonymous) @ level-controller.ts:376
(anonymous) @ level-controller.ts:200
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:429
onSuccess @ playlist-loader.ts:327
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:154
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ hls.ts:420
startHls @ 33:97
(anonymous) @ 33:147
Promise.then
(anonymous) @ 33:137
(anonymous) @ 33:150
33:101 HLS error {type: 'mediaError', details: 'bufferStalledError', fatal: false, error: Error: Playback stalling at @77.581342 due to low buffer ({"len":0,"start":77.581342,"end":77.58134…, buffer: 0, …}
(anonymous) @ 33:101
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ gap-controller.ts:234
(anonymous) @ gap-controller.ts:157
(anonymous) @ stream-controller.ts:946
(anonymous) @ stream-controller.ts:218
(anonymous) @ stream-controller.ts:213
(anonymous) @ task-loop.ts:106
setInterval
(anonymous) @ task-loop.ts:68
(anonymous) @ stream-controller.ts:127
(anonymous) @ hls.ts:434
(anonymous) @ hls.ts:433
(anonymous) @ level-controller.ts:376
(anonymous) @ level-controller.ts:200
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ playlist-loader.ts:429
onSuccess @ playlist-loader.ts:327
(anonymous) @ xhr-loader.ts:239
XMLHttpRequest.send
(anonymous) @ xhr-loader.ts:166
(anonymous) @ xhr-loader.ts:125
(anonymous) @ xhr-loader.ts:84
(anonymous) @ playlist-loader.ts:352
(anonymous) @ playlist-loader.ts:154
(anonymous) @ index.js:203
(anonymous) @ hls.ts:310
(anonymous) @ hls.ts:321
(anonymous) @ hls.ts:420
startHls @ 33:97
(anonymous) @ 33:147
Promise.then
(anonymous) @ 33:137
(anonymous) @ 33:150
33:101 HLS error {type: 'networkError', details: 'levelLoadTimeOut', fatal: false, url: 'https://test.inbi.ru/stream/hls/33?expires=1784764…26e1c6bf160df72c3c96b0e01ad11652b2f6f4c43c2dc9712', loader: t, …}

С этим можно что-то сделать?

## Assistant

~30 с — типично для HLS: длинный GOP у H.264 из браузера + буфер сегментов + медленный PHP-прокси на каждый запрос (особенно отдельный audio track). Сокращаю сегменты, форсирую keyframe и ускоряю прокси.

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\online_streams\\watch.php"}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Http\\Controllers\\StreamHlsController.php"}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\docker\\mediamtx\\mediamtx.yml"}
```

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

## Assistant

Да, можно. ~30 с и `bufferStalled` / `audioTrackLoadTimeOut` шли из трёх вещей:

1. **Редкие keyframe** у H.264 из браузера → сегменты HLS получались длинными  
2. **Обычный fMP4 HLS** + буфер 3+ сегмента  
3. **Медленный PHP-прокси** (на каждый кусок снова доступ + БД) → таймауты audio playlist

## Что изменено

| Место | Изменение |
|--------|-----------|
| MediaMTX | `hlsVariant: lowLatency`, сегменты 1 с, parts 300 мс |
| Мастер | `generateKeyFrame()` каждую 1 с (Chrome/Edge) |
| HLS-прокси | без повторного `canWatch`, кэш broadcast 3 с |
| Зритель | hls.js LL-режим, короче live buffer, не падать на stall |

Ожидаемая задержка после правок: **примерно 3–8 с** (для HLS это нормально). Субсекундной задержки у HLS не будет — для неё нужен WHEP/WebRTC у зрителя.

## На сервере

```bash
cd /var/www/inbi
sudo docker-compose down && sudo docker-compose up -d
php artisan config:clear
```

Мастер **перезапускает** эфир (лучше Chrome), зритель — hard refresh.

`bufferStalledError` / `bufferNudgeOnStall` с `fatal: false` можно не считать поломкой — плеер сам подтягивается. Если снова будут `*LoadTimeOut` — пришлите; следующий шаг — отдавать сегменты напрямую через nginx, минуя PHP.

## User

А расскажи, когда включается запись, а когда - онлайн трансляция? Может ли мастер заранее записать видео, чтобы его потом смотрели?

## Assistant

Сейчас у вас это **одна кнопка / один процесс**, а не два разных режима.

## Как работает сейчас

| Что | Когда включается |
|-----|------------------|
| **Онлайн-трансляция** | Мастер в кабинете нажимает «Начать трансляцию» → камера идёт в MediaMTX → зрители смотрят HLS |
| **Запись** | **Автоматически вместе с эфиром** — MediaMTX пишет на диск, пока идёт публикация |
| **Конец записи** | Мастер жмёт «Завершить» → сессия WHIP закрывается → через несколько секунд `FinalizeStreamRecordingJob` ищет файл и сохраняет в `stream_recordings` |

То есть запись — это не отдельная кнопка, а **побочный продукт live-эфира**: нет эфира → нет этой серверной записи.

Кнопка «Начать» доступна только в окне занятия (день недели + время ±15 мин до старта и до конца).

## Может ли мастер заранее записать видео «на потом»?

**В текущей реализации — нет.**  
Нет сценария: «записать сейчас без зрителей / вне слота → опубликовать как VOD».

Что есть по факту:
- запись появляется только если был live;
- после эфира файл может открываться на странице занятия (если запись нашлась и `published`);
- заранее залить готовое видео как «эфир на потом» — не сделано.

## Как это обычно делают, если нужно

1. **Предзапись (VOD)** — мастер загружает MP4 в админке/кабинете, зрители смотрят файл (без MediaMTX live).  
2. **«Репетиция»** — мастер запускает тот же live вне расписания (нужно ослабить окно времени), запись сохранится, потом её показывают как запись.  
3. **Гибрид** — live как сейчас + отдельно библиотека загруженных уроков.

Если нужно именно «записать заранее и раздать позже» — это отдельная фича (загрузка видео + привязка к `Schedule` + страница просмотра без WHIP). Могу набросать, как встроить в текущую схему.

## User

Теперь у ведущего пишет ошибку: Ошибка: WHIP: сервер вернул 502

## Assistant

502 на WHIP почти всегда значит: nginx не достучался до MediaMTX на `:8889`. Проверяю конфиг и типичные причины после перехода на `network_mode: host`.

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\docker-compose.yml"}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\docker\\mediamtx\\mediamtx.yml"}
```

### Tool Mcp

```
{"name":"user-context7-query-docs","args":{"libraryId":"/bluenviron/mediamtx","query":"hlsPartHoldBack hlsVariant lowLatency configuration parameters valid yaml"},"toolCallId":"call-f905b0b6-117f-45a5-b775-543dc5d6d709-226\nfc_81a8dfc8-58a0-9366-b7c1-0cb4a96d985e_2","providerIdentifier":"context7","toolName":"query-docs","smartModeApprovalOnly":false,"skipApproval":false}
```

## Assistant

Убираю из конфига параметры, из‑за которых MediaMTX 1.11 может не стартовать — тогда nginx и отдаёт 502.

### Tool Edit File V2

## Assistant

**502** = nginx проксирует на `127.0.0.1:8889`, а MediaMTX там не отвечает (упал / не запустился / порт занят).

Частая причина после прошлого обновления — в `mediamtx.yml` были поля вроде `hlsPartHoldBack` / `hlsSegmentMaxSize`, из‑за которых MediaMTX 1.11 может **не стартовать**. Конфиг упрощён.

## На сервере выполните

```bash
cd /var/www/inbi

# 1. Обновить mediamtx.yml, затем:
sudo docker-compose down
sudo docker-compose up -d

# 2. Жив ли контейнер?
sudo docker ps -a | grep mediamtx
sudo docker logs inbi-mediamtx --tail 40

# 3. Слушает ли 8889?
sudo ss -tlnp | grep -E '8888|8889|8189'

# 4. Отвечает ли WHIP напрямую (минуя nginx)?
curl -sI http://127.0.0.1:8889/
curl -sI https://stream.inbi.ru/
```

В логах должно быть:
```text
INF configuration loaded from /mediamtx.yml
INF [WebRTC] listener opened on :8889
```

Если вместо этого `ERR` / `unsupported parameter` — пришлите вывод `docker logs`.

## Если контейнер Restarting / Exited

```bash
sudo docker-compose logs --tail 80
```

Часто после `network_mode: host` порт уже занят старым процессом:

```bash
sudo ss -tlnp | grep 8889
# если висит старый mediamtx / другой процесс — убить и снова up
```

## Если `curl http://127.0.0.1:8889/` ок, а WHIP с сайта 502

Смотрите nginx:

```bash
sudo tail -30 /var/log/nginx/error.log
```

Там будет `connect() failed` к upstream — значит proxy_pass смотрит не туда (нужен `http://127.0.0.1:8889`).

---

После того как `curl -sI http://127.0.0.1:8889/` отвечает не 502/connection refused — мастер снова «Начать трансляцию». Пришлите вывод пунктов 2–4, если не оживёт.

## User

Теперь мастер видит: Эфир идёт — зрители смотрят через HLS
А зритель: Плейлист: HTTP 503 — MediaMTX ещё не отдаёт плейлист. [http_404]

root@vm736755:/var/www/inbisudo docker-compose logs --tail 8080
Attaching to inbi-mediamtx
inbi-mediamtx | 2026/07/22 20:04:28 INF MediaMTX v1.11.3
inbi-mediamtx | 2026/07/22 20:04:28 INF configuration loaded from /mediamtx.yml
inbi-mediamtx | 2026/07/22 20:04:28 INF [RTSP] listener opened on :8554 (TCP), :8000 (UDP/RTP), :8001 (UDP/RTCP)
inbi-mediamtx | 2026/07/22 20:04:28 INF [RTMP] listener opened on :1935
inbi-mediamtx | 2026/07/22 20:04:28 INF [HLS] listener opened on :8888
inbi-mediamtx | 2026/07/22 20:04:28 INF [WebRTC] listener opened on :8889 (HTTP), :8189 (ICE/UDP), :8189 (ICE/TCP)
inbi-mediamtx | 2026/07/22 20:04:28 INF [SRT] listener opened on :8890 (UDP)
inbi-mediamtx | 2026/07/22 20:04:28 INF [API] listener opened on :9997
inbi-mediamtx | 2026/07/22 20:05:03 INF [WebRTC] [session 5aa62d74] created by 127.0.0.1:53114
inbi-mediamtx | 2026/07/22 20:05:14 INF [WebRTC] [session 5aa62d74] peer connection established, local candidate: host/udp/172.18.0.1/8189, remote candidate: prflx/udp/178.157.135.193/17994
inbi-mediamtx | 2026/07/22 20:05:14 INF [path live/i3zt6yzj0akkatsid8pzez6i6uhnqagq] [recorder] recording 2 tracks (Opus, H264)
inbi-mediamtx | 2026/07/22 20:05:14 INF [WebRTC] [session 5aa62d74] is publishing to path 'live/i3zt6yzj0akkatsid8pzez6i6uhnqagq', 2 tracks (Opus, H264)
inbi-mediamtx | 2026/07/22 20:05:14 INF [HLS] [muxer live/i3zt6yzj0akkatsid8pzez6i6uhnqagq] created automatically
inbi-mediamtx | 2026/07/22 20:05:14 ERR [HLS] [muxer live/i3zt6yzj0akkatsid8pzez6i6uhnqagq] Low-Latency HLS requires at least 7 segments
inbi-mediamtx | 2026/07/22 20:05:24 ERR [HLS] [muxer live/i3zt6yzj0akkatsid8pzez6i6uhnqagq] Low-Latency HLS requires at least 7 segments
inbi-mediamtx | 2026/07/22 20:05:34 ERR [HLS] [muxer live/i3zt6yzj0akkatsid8pzez6i6uhnqagq] Low-Latency HLS requires at least 7 segments
inbi-mediamtx | 2026/07/22 20:05:44 ERR [HLS] [muxer live/i3zt6yzj0akkatsid8pzez6i6uhnqagq] Low-Latency HLS requires at least 7 segments
inbi-mediamtx | 2026/07/22 20:05:54 ERR [HLS] [muxer live/i3zt6yzj0akkatsid8pzez6i6uhnqagq] Low-Latency HLS requires at least 7 segments
inbi-mediamtx | 2026/07/22 20:06:04 ERR [HLS] [muxer live/i3zt6yzj0akkatsid8pzez6i6uhnqagq] Low-Latency HLS requires at least 7 segments
inbi-mediamtx | 2026/07/22 20:06:14 ERR [HLS] [muxer live/i3zt6yzj0akkatsid8pzez6i6uhnqagq] Low-Latency HLS requires at least 7 segments
root@vm736755:/var/www/inbi#

## Assistant

В логах прямо указано: Low-Latency HLS требует минимум 7 сегментов — у нас было 4. Исправляю.

### Tool Edit File V2

## Assistant

Причина в логе:

```text
Low-Latency HLS requires at least 7 segments
```

В конфиге было `hlsSegmentCount: 4` — muxer падал, плейлист не отдавался → 404/503.

Исправлено на **`hlsSegmentCount: 7`**.

```bash
cd /var/www/inbi
# обновить mediamtx.yml
sudo docker-compose down && sudo docker-compose up -d
```

Мастер: завершить эфир → начать снова. Зритель: обновить страницу.

В логах должно появиться `is converting into HLS`, без этой ERR.

## User

Убери текст "Поток уходит на медиасервер в H.264 (нужно для HLS). Рекомендуется Chrome или Edge. В логах MediaMTX должно быть: converting into HLS, 2 tracks (H264, Opus) — не VP8."

### Tool Edit File V2

## Assistant

Готово — этот текст убран со страницы трансляции мастера.

## User

1. Можно ли как-то качество выбирать? Или это дополнительная нагрузка на сервер?
2. Блок прогресса видео выглядит очень криво при трансляции - то прыгает, то рандомно меняет цифры и т.п. Можно его вообще убрать?

## Assistant

По качеству кратко отвечу в конце; прогресс-бар у live уберу — для HLS он и ведёт себя так.

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\online_streams\\watch.php","limit":50}
```

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

## Assistant

### 1. Выбор качества

**Сейчас — нет.** Идёт один поток: то, что кодирует браузер мастера (H.264), MediaMTX его только перекладывает в HLS, **без перекодирования**.

Несколько качеств (360p / 720p / 1080p) = **доп. нагрузка на сервер**:
- ffmpeg/транскодинг нескольких битрейтов (CPU/GPU заметно);
- или несколько потоков от мастера (на практике почти не делают).

Без транскодинга можно лишь косвенно влиять: разрешение/битрейт камеры у мастера. Отдельный селектор качества — отдельная фича с медиасервером/транскодером.

### 2. Полоса прогресса

У live HLS время «плывёт», поэтому нативный scrubber прыгает — это нормально для эфира, не баг плеера.

Убрал стандартные `controls`. Для live остались только:
- Пауза / Смотреть  
- Звук  
- На весь экран  
- бейдж LIVE  

У **записей** (не live) обычные controls с прогрессом по-прежнему есть.

## User

Скажи примерно, какие системные требования нужны, чтобы хотя бы для 10-20 зрителей это работало?
И могут ли 2 мастера одновременно проводить трансляции разных мероприятий?

## Assistant

## Системные требования (10–20 зрителей)

У вас схема: **1 мастер → MediaMTX (remux, без транскодинга) → HLS через Laravel-прокси**. Нагрузка на CPU небольшая; упираетесь в **сеть** и в то, что сегменты сейчас идут через PHP.

### Минимум (нормально для 10–20)

| Ресурс | Ориентир |
|--------|----------|
| **CPU** | 2 vCPU (лучше 4) |
| **RAM** | 4 GB (спокойно 8 GB, если рядом сайт + MySQL + nginx) |
| **Диск** | SSD, + место под записи (~1–2 GB/час на эфир) |
| **Канал исходящий** | **50–100 Мбит/с** запас |

Примерная исходящая полоса на зрителя при ~1.5–2.5 Мбит/с видео:

- 10 зрителей ≈ **15–25 Мбит/с**
- 20 зрителей ≈ **30–50 Мбит/с**

Плюс WHIP от мастера (~2–4 Мбит/с входящих) и запись на диск.

Ваш текущий VPS, на котором уже крутятся сайт + nginx + MySQL + MediaMTX, для **10–20** обычно хватает, если канал не урезан.

### Узкие места именно вашей сборки

1. **Laravel-прокси HLS** — каждый сегмент/плейлист через PHP. На 10–20 ещё терпимо; на 50+ лучше отдавать HLS напрямую nginx’ом (с проверкой доступа).
2. **Канал сервера** — чаще упираются сюда, а не в CPU.
3. **Запись** — почти не грузит CPU (remux), только диск.

Транскодинг нескольких качеств **не включён** — это сильно снижает требования к CPU.

---

## Два мастера одновременно?

**Да.** У каждого эфира свой `stream_key` и путь `live/{key}` в MediaMTX. Два мастера = два независимых publisher’а.

Учтите запас:

| | 1 эфир | 2 эфира сразу |
|--|--------|----------------|
| Входящий WHIP | ~2–4 Мбит/с | ~4–8 Мбит/с |
| Исходящий HLS | зрители × битрейт | сумма по обоим |
| CPU MediaMTX | низкий | всё ещё низкий без транскода |
| Диск (запись) | 1 поток | 2 потока |

Если у каждого по 10 зрителей (итого 20) — те же ориентиры, что выше. Если у каждого по 20 (итого 40) — целитесь в **~80–100 Мбит/с** исходящих и лучше 4 vCPU / 8 GB.

В коде нет блокировки «только один эфир на весь сайт». Ограничение только у мастера: свои онлайн-занятия в своём кабинете; два разных мастера могут вещать параллельно без конфликта путей.

---

**Кратко:** для 10–20 зрителей достаточно скромного VPS (2–4 CPU, 4–8 GB, SSD) и стабильных **~50+ Мбит/с** наружу. Два мастера одновременно — да, это штатный режим MediaMTX.

## User

А почему у нас сейчас идет проксирование через laravel, а не напрямую nginx? Ради авторизации?

## Assistant

Да. Прокси через Laravel сделан **ради контроля доступа и подписи ссылок**, а не потому что MediaMTX так требует.

## Зачем Laravel в цепочке

1. **Проверка права смотреть** — `public` / `authenticated` / `abonement` (логин, абонемент).
2. **Подписанный URL** — `/stream/hls/...?file=...&signature=...`, чтобы нельзя было просто угадать путь `http://server:8888/live/{key}/index.m3u8`.
3. **Порт 8888 не торчит наружу** — зритель ходит только на `test.inbi.ru`, MediaMTX HLS остаётся на localhost.
4. **Переписывание плейлиста** — сегменты и `audio*.m3u8` тоже через те же подписанные ссылки (иначе hls.js полез бы на открытый MediaMTX).

То есть да: **авторизация + секретность stream_key**, одним местом в PHP.

## Минусы

Каждый `.m3u8` / сегмент → PHP → HTTP к MediaMTX. На 10–20 зрителях ок, на десятках+ — лишняя задержка, CPU и те самые timeout’ы.

## Как делают «правильно» на проде

Nginx отдаёт HLS **напрямую**, а Laravel только выдаёт доступ:

```text
Зритель → nginx (/hls/...) → MediaMTX :8888
                ↑
         auth_request / secure_link
         (Laravel отвечает 200/403)
```

Или: одноразовый JWT/cookie после `/online-streams/{id}`, дальше nginx пускает по нему без прогона каждого байта через PHP.

**Итог:** сейчас Laravel-прокси — осознанный упрощённый вариант «доступ + закрытый MediaMTX». Для масштаба и меньшей задержки имеет смысл вынести отдачу сегментов на nginx, оставив Laravel только на проверку права.

## User

Сейчас зритель видит намного менее плавно (меньше кадров в секунду), чем я вижу в окне мастера. С чем это связано?

## Assistant

Это ожидаемо при вашей схеме, а не баг «сломался плеер».

## Почему у мастера плавно, у зрителя — нет

**У мастера** в окне — локальный preview с камеры (`getUserMedia`). Кадры идут сразу с камеры в `<video>`, без сети и без HLS. Обычно 24–30 fps.

**У зрителя** путь другой:

```text
камера → H.264 в браузере → WebRTC → MediaMTX → нарезка HLS → Laravel → hls.js → экран
```

По дороге теряется плавность из‑за:

1. **Кодирование H.264 в браузере** — битрейт и FPS ограничены (часто 15–24 fps, особенно по CPU/вебкам).
2. **HLS + LL-HLS** — поток режется на сегменты/parts; плеер буферизует и иногда «подтягивает» время → ощущение рывков.
3. **Редкие keyframe** — между ключевыми кадрами HLS/декодер хуже переживает потери; мы шлём keyframe раз в секунду, но это не полноценные 30 fps «как локально».
4. **Задержка и буфер** — при нехватке буфера hls.js делает stall/nudge → краткие подвисания.
5. **PHP-прокси** — лишняя задержка на сегментах может усугублять рывки (не главная причина FPS, но влияет на «плавность»).

Локальное окно мастера **не показывает**, что реально уходит в эфир. Для сравнения смотрите глазами зрителя или второй вкладкой как зритель.

## Что можно улучшить (по эффекту)

| Мера | Эффект |
|------|--------|
| Мастер: Chrome/Edge, поменьше вкладок, лучше свет/вебка | Стабильнее FPS кодирования |
| Короткий GOP / частые keyframe (уже частично есть) | Меньше «квадратов» и рывков |
| Отдача HLS через nginx, не через Laravel | Ровнее доставка сегментов |
| WHEP/WebRTC у зрителя вместо HLS | Ближе к «живому» FPS, задержка 1–2 с |
| Транскод на сервере (ffmpeg) | Контроль fps/битрейта, но нагрузка на CPU |

**Кратко:** мастер смотрит сырую камеру; зритель — сжатый HLS с задержкой и буфером. Меньше fps и «неровность» — свойство текущего пайплайна, не того, что камера у мастера «рисует» 30 fps на экране.

## User

Почему сейчас не сделано WHEP/WebRTC у зрителя вместо HLS? Какие у текущей реализации преимущества и недостатки?

## Assistant

WHEP не сделали не потому что «нельзя», а потому что вы просили другое: **сервер + HLS + доступ + запись + много зрителей**. Под это HLS подходит лучше, чем WebRTC на каждого зрителя.

## Почему сейчас HLS, а не WHEP

| Ваш запрос | HLS | WHEP (WebRTC зрителю) |
|------------|-----|------------------------|
| Через сервер | да | да (SFU/ретрансляция) |
| Контроль доступа | удобно (подписанный `.m3u8` / nginx) | сложнее (токен на каждый peer/сессию) |
| Запись | MediaMTX пишет с publisher «из коробки» | запись тоже возможна, но зрительский протокол на неё не влияет |
| ~50 зрителей | один исходящий HLS, масштабируется проще | на каждого — отдельное WebRTC; нагрузка растёт быстрее |
| Простая выдача | hls.js / Safari native | свой JS + ICE/TURN снова |

Итог: HLS закрыл ваши три цели одним пайплайном. WHEP дал бы **плавность и задержку**, но усложнил бы доступ, NAT и нагрузку.

---

## Текущая реализация (мастер WHIP → MediaMTX → HLS зрителю)

### Плюсы
- **Масштаб 10–50+** без N исходящих WebRTC с сервера на каждого
- **Запись** рядом с тем же ingest
- **Доступ** через Laravel (подписи, абонемент) без открытия `:8888`
- **Один поток** от мастера, без транскода (CPU сервера низкий)
- **Знакомый стек** — браузер + hls.js, меньше возни с ICE у зрителей

### Минусы
- **Задержка** секунды (сейчас порядка нескольких–десятков)
- **Плавность/FPS** хуже, чем локальный preview мастера
- **Полоса прогресса** у live бессмысленна (уже убрали)
- **Прокси через PHP** — лишняя задержка/таймауты под нагрузкой
- **Один битрейт** — без выбора качества
- Зритель зависит от HLS-сегментов и буфера (stall/nudge)

---

## Если бы был WHEP у зрителя

### Плюсы
- Задержка **~0.5–2 с**
- Картинка ближе к «как у мастера»
- Меньше ощущения «телепередача с буфером»

### Минусы
- Снова **ICE/UDP/TURN** у каждого зрителя (у вас уже болело на WHIP)
- Нагрузка на MediaMTX **линейно по числу зрителей**
- Авторизация и «закрытый поток» сложнее, чем signed HLS
- Запись от этого не упрощается — она и так от publisher

---

## Практичный вывод

- **HLS** — «занятие на 10–50 человек, запись, доступ, терпимая задержка».
- **WHEP** — «почти как Zoom по задержке», но дороже в эксплуатации и хрупче по сети.

Гибрид на будущее: live смотреть по **WHEP**, запись/догоняющих/архив — по **HLS/VOD**. Сейчас сознательно выбран путь под ваше ТЗ про сервер, доступ, запись и число зрителей.

## User

Сделай окно чата справа у мастера и пользователей. С возможностью писать зрителям, а мастер бы это увидел. Сохранять его нигде не надо - достаточно, чтобы он был в моменте трансляции

## Assistant

Добавлю эфемерный чат через cache (без БД): сайдбар у мастера и зрителя, polling новых сообщений.

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\master\\cabinet\\broadcast.php","limit":50}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\online_streams\\watch.php","limit":80}
```

### Tool Ripgrep Raw Search

```
{"pattern":"master\\.cabinet|online_streams|stream\\.","path":"c:\\OSPanel\\home\\inbi.loc\\routes\\web.php","caseInsensitive":false,"multiline":false,"toolCallId":"call-18dffc8b-05a3-4817-9cc8-be285bb9eb7e-236\nfc_27764bad-145d-91ac-b0ab-2265a2842a81_2","offset":0}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Models\\User.php","limit":40}
```

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Ripgrep Raw Search

```
{"pattern":"online-streams|master/cabinet|StreamHls","path":"c:\\OSPanel\\home\\inbi.loc\\routes","caseInsensitive":false,"multiline":false,"toolCallId":"call-750a1a6b-1453-4306-b678-35c841da8b90-3\nfc_8e092b8b-4d45-9426-8346-b4c4098c19e7_0","offset":0}
```

### Tool Ripgrep Raw Search

```
{"pattern":"stopBroadcast|activeLiveBroadcast|function canWatch","path":"c:\\OSPanel\\home\\inbi.loc\\app","caseInsensitive":false,"multiline":false,"toolCallId":"call-750a1a6b-1453-4306-b678-35c841da8b90-4\nfc_8e092b8b-4d45-9426-8346-b4c4098c19e7_1","offset":0}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\master\\cabinet\\broadcast.php","limit":80}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\online_streams\\watch.php","limit":80}
```

### Tool Get Mcp Tools

```
{"server":"user-context7","toolName":"resolve-library-id","toolCallId":"call-750a1a6b-1453-4306-b678-35c841da8b90-7\nfc_8e092b8b-4d45-9426-8346-b4c4098c19e7_4"}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Http\\Controllers\\MasterCabinetController.php","offset":100,"limit":80}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Http\\Controllers\\OnlineStreamController.php"}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\routes\\web.php","offset":325,"limit":50}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\online_streams\\watch.php","offset":80}
```

### Tool Ripgrep Raw Search

```
{"pattern":"full_name|first_name|fillable","path":"c:\\OSPanel\\home\\inbi.loc\\app\\Models\\User.php","caseInsensitive":false,"multiline":false,"toolCallId":"call-1144a86f-6259-4026-be4c-90467ceba0c9-12\nfc_7b6ccc09-995d-92e7-a35f-6c5e2e2c873c_4","offset":0}
```

### Tool Mcp

```
{"name":"user-context7-resolve-library-id","args":{"libraryName":"Laravel","query":"Cache put get array TTL Laravel 11"},"toolCallId":"call-1144a86f-6259-4026-be4c-90467ceba0c9-13\nfc_7b6ccc09-995d-92e7-a35f-6c5e2e2c873c_5","providerIdentifier":"context7","toolName":"resolve-library-id","smartModeApprovalOnly":false,"skipApproval":false}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Models\\User.php","limit":80}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Http\\Controllers\\MasterCabinetController.php","limit":50}
```

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Edit File V2

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\routes\\web.php","limit":30}
```

### Tool Ripgrep Raw Search

```
{"pattern":"csrf-token","path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\layouts","caseInsensitive":false,"multiline":false,"toolCallId":"call-c4cd5931-d05c-4573-8b5c-a2fbc9960c39-28\nfc_5a84cb3d-5c68-9c96-9428-2148daa2e99c_1","offset":0}
```

### Tool Edit File V2

### Tool Run Terminal Command V2

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\app\\Services\\Streaming\\StreamChatService.php"}
```

### Tool Ripgrep Raw Search

```
{"pattern":"CACHE_","path":"c:\\OSPanel\\home\\inbi.loc\\.env","caseInsensitive":false,"multiline":false,"toolCallId":"call-b59abfc8-1bd6-4d4a-b10b-8eb0674798a4-32\nfc_73024080-e38d-9a3f-bdc1-ba358cdf2a8b_1","offset":0}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\online_streams\\watch.php","limit":60}
```

### Tool Read File V2

```
{"path":"c:\\OSPanel\\home\\inbi.loc\\resources\\views\\master\\cabinet\\broadcast.php","limit":55}
```

## Assistant

Готово: справа от видео у мастера и зрителей появился чат на время эфира.

**Как работает**
- Зрители пишут сообщения, мастер видит их (и может отвечать).
- Сообщения живут только в Cache, в БД не пишутся.
- После «Завершить трансляцию» чат очищается.
- Гостю без имени показывается поле «Ваше имя»; у авторизованных берётся имя из профиля.

Чат виден только пока трансляция live.