diff --git a/changelog.json b/changelog.json index 936d110c..17fdb86e 100644 --- a/changelog.json +++ b/changelog.json @@ -1,34 +1,33 @@ { - "version": "2.5.0", + "version": "2.5.1", "changes": [ - "Completed the large-scale migration of the admin interface to Bootstrap 5", - "Rebuilt the admin shell with a modern responsive layout", - "Migrated administration tables to clean JSON-based data endpoints", - "Migrated streams, VOD, series, episodes, users, devices, servers, EPG, backups, security, logs and other management pages to Bootstrap 5", - "Added Bootstrap 5 modal and tabbed form layouts", - "Migrated the reseller panel to the Bootstrap 5 interface", - "Redesigned admin and reseller login pages with a sci-fi HUD-style interface", - "Rebuilt the admin dashboard using Bootstrap 5 statistics cards", - "Added a world map visualization of active connections", - "Added per-user UI customization persisted in users.ui_prefs", - "Added global quick search with structured JSON results", - "Reorganized the admin sidebar and navigation", - "Added xcToast notifications to replace native browser alerts", - "Added module provider contracts for topbar, tables, permissions and quick tools", - "Improved module controller and REST integration", - "Added configurable xc_fanout source backend", - "Added per-repository update channels (Stable/Beta) for the panel, binaries and the xc_fanout daemon", - "Added xc_fanout version tracking and integrity probing", - "Improved HLS MEDIA-SEQUENCE handling across off-air and live transitions", - "Improved stream entry point readability and stability", - "Added synthetic input-free test patterns to the stream generator", - "Improved VOD analyzer stability under PHP 8", - "Added Playwright end-to-end smoke testing for the Bootstrap 5 admin", - "Expanded test coverage for migrated admin tables", - "Improved GeoIP release synchronization", - "Fixed numerous admin UI, PHP 8, null-safety and streaming issues", - "Updated contributor, module, FFmpeg and architecture documentation", - "Removed obsolete DTS/AC3 testing helpers", - "Updated PHPStan and other dependencies" + "Added a complete Smart Activation Codes system with pre-generated voucher codes, delayed first-use activation, automatic subscriber line creation, batch management, mass editing, exports, admin/reseller interfaces, an activation portal and a public activation API.", + "Added transactional activation-code generation with reseller credit validation, balance deductions and audit logging.", + "Added configurable xc_fanout encoder supervision so live stream encoders can be managed and monitored by the streaming daemon instead of a separate PHP watchdog process.", + "Added the fanout_supervise setting with a safe disabled default for existing installations and synchronized the setting into the daemon configuration.", + "Added supervision reconciliation, health policies, source forcing and daemon-aware process management.", + "Added reliable IngestFeeder support for PHP-based producers feeding MPEG-TS data into xc_fanout.", + "Fixed short non-blocking socket writes, preventing incomplete TS packets from being silently discarded.", + "Added daemon reconnect and re-registration handling with packet-aligned recovery after connection failures or daemon restarts.", + "Added bounded ingest buffering with whole-packet shedding to prevent producers from blocking indefinitely.", + "Added HLS key and IV propagation for encrypted streams handled through the fanout ingest path.", + "Moved live encoder supervision and stream ownership handling further into the xc_fanout architecture.", + "Improved on-demand stream startup by using the configured on_demand_wait_time and clearing stale PID and monitor files before restarting a stream.", + "Fixed asynchronous file-operation caching and inotify timeout behavior, including stale cache invalidation when files appear.", + "Improved stream monitoring with FPS-drop restarts, better backup-source priority handling and failed-probe recovery.", + "Fixed catch-up routing, proxy-only checks and adaptive stream variant authentication behavior.", + "Corrected VOD and catch-up byte ranges to provide proper seeking and delivery behavior.", + "Fixed live HLS playlist sequence handling so playlists are not unnecessarily renumbered when segment durations exceed 10 seconds.", + "Improved daemon viewer connection-limit enforcement and viewer kick handling.", + "Added producer, CPU and memory information to the admin streams page.", + "Improved stream supervision metadata so the panel correctly records which producer and process path owns a stream.", + "Added version-at-error-time information to panel logs for more accurate troubleshooting of historical errors.", + "Added new streaming documentation covering daemon feeds, kicks, HLS keys, byte ranges, stream supervision and the native remuxer.", + "Added a shared synthetic test-stream generator with a TS encoder, keyframe-aligned joins and launcher support.", + "Added and expanded fanout supervision and ingest tests.", + "Added the database migration required for fanout_supervise and updated related settings handling.", + "Fixed duplicate page selectors on mass operation pages.", + "Added safeguards preventing update and rollback operations from being executed without root privileges.", + "Simplified sourceContext value retrieval and integrated upstream/community changes from pull requests #186 and #187." ] } diff --git a/docs/ru/builds/updates_checklist.md b/docs/ru/builds/updates_checklist.md index b8db3f07..dd4e52e5 100644 --- a/docs/ru/builds/updates_checklist.md +++ b/docs/ru/builds/updates_checklist.md @@ -20,8 +20,11 @@ git log --pretty=format:"- %s (%h)" "$PREV_TAG"..main > dist/changes.md ```json { - "version": "X.Y.Z", - "changes": ["Description of change 1", "Description of change 2"] + "version": "X.Y.Z", + "changes": [ + "Description of change 1", + "Description of change 2" + ] } ``` @@ -60,15 +63,15 @@ make docs-build # strict build — fails on any broken link/anchor ``` - `make docs-translate` повторно переводятся только те файлы на английском языке, содержимое которых - изменен (для каждого файлового кэша), так что при постепенном выпуске это происходит быстро. +изменен (для каждого файлового кэша), так что при постепенном выпуске это происходит быстро. - **Review and commit the regenerated `docs/ru`** — он включен в единый - снимите фиксацию (шаг 5). Никогда не редактируйте вручную `docs/ru`. -- Документация публикуется **на каждый релиз, а не на каждый push**: `pages.yml` - запускается при пуше версионного **тега** (шаг 7) и публикует документы этого выпуска - как версионный снимок (`X.Y.Z` + псевдоним `latest`) в ветку `gh-pages` через - `mike`. Правки, влитые в `main` между релизами, попадают в публикацию на следующем - помеченном тегом релизе (тогда же регенерируется `docs/ru`). Селектор версий в шапке - Material позволяет читателям переключаться между выпущенными версиями. +снимите фиксацию (шаг 5). Никогда не редактируйте вручную `docs/ru`. +- Documentation is published **per release, not per push**: `pages.yml` runs when +будет запущена версия **метка** (шаг 7), и документы этого выпуска будут опубликованы в виде +версионный снимок (`X.Y.Z` + псевдоним `latest`) в ветвь `gh-pages` через +`mike`. Правки, объединенные в `main` между выпусками, будут опубликованы на следующем помеченном +высвобождение (которое также происходит при регенерации `docs/ru`). Заголовок материала +селектор версий позволяет читателям переключаться между выпущенными версиями. --- @@ -131,6 +134,7 @@ cat src/migrations/deleted_files.txt > любой, кто доберется до панели. Этот шаг является мерой усиления безопасности — разблокировка никогда не должна > выходи на улицу в нем. + ```text src/Core/Config/AppConfig.php ``` @@ -171,12 +175,12 @@ make main После построения `dist/` должно содержать: -| Файл | Описание | -| --------------------- | ------------------------------------------------------ | -| `XC_VM.zip` | ОСНОВНОЙ установщик (установить скрипт + xc_vm.tar.gz) | -| `xc_vm.tar.gz` | ОСНОВНОЙ архив (установка и обновление) | -| `loadbalancer.tar.gz` | Архив LB (установка и обновление) | -| `hashes.md5` | Контрольные суммы MD5 | +|Файл|Описание| +| --- | --- | +| `XC_VM.zip` |ОСНОВНОЙ установщик (установить скрипт + xc_vm.tar.gz)| +| `xc_vm.tar.gz` |ОСНОВНОЙ архив (установка и обновление)| +| `loadbalancer.tar.gz` |Архив LB (установка и обновление)| +| `hashes.md5` |Контрольные суммы MD5| > Один и тот же архив используется как для чистой установки, так и для обновлений. > Скрипт обновления (`src/update`) отфильтровывает двоичные/конфигурационные каталоги во время выполнения, используя жестко заданный список `UPDATE_EXCLUDE_DIRS` внутри самого скрипта Python. @@ -213,7 +217,7 @@ bash tools/test-install/test_release.sh - Прикрепите их к фиксатору - Отправьте уведомление в Telegram через `release-notifier.yml` - Опубликуйте документацию по этой версии на страницах GitHub через `pages.yml` (mike): - моментальный снимок `X.Y.Z` плюс псевдоним `latest`, который можно выбрать из заголовка документа +моментальный снимок `X.Y.Z` плюс псевдоним `latest`, который можно выбрать из заголовка документа > ✅ Дождитесь завершения рабочего процесса действий, затем убедитесь, что все файлы доступны для загрузки. @@ -226,6 +230,7 @@ bash tools/test-install/test_release.sh > в **та же версия** — LBs считывается база данных MAIN, и может возникнуть перекос в схеме/поведении > потоковый. Не оставляйте LBs без внимания. + - [ ] Убедитесь, что все 4 ресурса присоединены к релизу - [ ] Выполнить `md5sum -c hashes.md5` для загруженных файлов - [ ] Проверьте, отправлено ли уведомление Telegram @@ -236,14 +241,14 @@ bash tools/test-install/test_release.sh ## Если что-то пойдет не так - **После публикации не удалось выполнить построение действий** — в релизе отсутствуют (или частично) ресурсы. Повторно запустите - сбой рабочего процесса на вкладке Действия; если сам тег неверен, удалите выпуск **и**, который - пометьте (`git push --delete origin vX.Y.Z`), исправьте и пометьте повторно. Не оставляйте опубликованный релиз с - отсутствующие ресурсы — панели извлекают из него `hashes.md5` / архивы. +сбой рабочего процесса на вкладке Действия; если сам тег неверен, удалите выпуск **и**, который +пометьте (`git push --delete origin vX.Y.Z`), исправьте и пометьте повторно. Не оставляйте опубликованный релиз с +отсутствующие ресурсы — панели извлекают из него `hashes.md5` / архивы. - **Выпущенный актив поврежден** — опубликовать выпуск исправления **заплатка** (новый тег) вместо редактирования - опубликованный файл; клиенты прикрепляют его к тегу. +опубликованный файл; клиенты прикрепляют его к тегу. - **Плохой релиз уже достиг серверов** — операторы могут понизить рейтинг каждого сервера с помощью панели управления - (**Серверы → Откат версии**, см. [Механизм обновления → Откат](../administration/update-system.md#rollback-downgrade)); в MAIN сначала автоматически создается резервная копия базы данных. Миграции выполняются - доступна только переадресация, поэтому, если исправление небольшое, предпочитайте исправление с переадресацией. +(**Серверы → Откат версии**, см. [Механизм обновления → Откат](../administration/update-system.md#rollback-downgrade)); в MAIN сначала автоматически создается резервная копия базы данных. Миграции выполняются +доступна только переадресация, поэтому, если исправление небольшое, предпочитайте исправление с переадресацией. --- @@ -253,32 +258,32 @@ bash tools/test-install/test_release.sh **Проверка качества** — сначала запустите `make dev-tools`, затем `make dev-clean`, когда закончите: -| Команда | Цель | -| ------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | -| `make dev-tools` | Установите инструменты разработки (PHPStan, phpcs) через `composer install` | -| `make phpstan` | Статический анализ (также выявляет синтаксические ошибки) | -| `make phpstan-baseline` | Восстановите базовую линию PHPStan | -| `make cs` | Проверка стиля кода - импорт/гигиена пространства имен (phpcs + Slevomat) | -| `make cs-fix` | Примените исправления в стиле кода на месте | -| `make gates` | PSR-4 регрессионные шлюзы (для процедурного использования, для LB-архива, только для продукта поставщика) | -| `make dev-clean` | Снова удалите инструменты разработки, восстановив только производственную версию `vendor/`. | -| `php tools/.bin/phpunit.phar -c tests/phpunit.xml.dist` | Модульные тесты | +|Команда|Цель| +| --- | --- | +| `make dev-tools` |Установите инструменты разработки (PHPStan, phpcs) через `composer install`| +| `make phpstan` |Статический анализ (также выявляет синтаксические ошибки)| +| `make phpstan-baseline` |Восстановите базовую линию PHPStan| +| `make cs` |Проверка стиля кода - импорт/гигиена пространства имен (phpcs + Slevomat)| +| `make cs-fix` |Примените исправления в стиле кода на месте| +| `make gates` |PSR-4 регрессионные шлюзы (для процедурного использования, для LB-архива, только для продукта поставщика)| +| `make dev-clean` |Снова удалите инструменты разработки, восстановив только производственную версию `vendor/`.| +| `php tools/.bin/phpunit.phar -c tests/phpunit.xml.dist` |Модульные тесты| **Release prep & build:** -| Команда | Цель | -| ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | -| `make generate_deleted_files` | Регенерировать `src/migrations/deleted_files.txt` | -| `make new` | Wipe + recreate `dist/` — запускается ОДИН раз в начале (шаг 1), перед записью `dist/changes.md`; никогда больше перед созданием | -| `make lb` | Создайте архив LoadBalancer в виде `dist/` | -| `make main` | Соберите ОСНОВНОЙ архив в `dist/` | -| `bash tools/test-install/test_release.sh` | Установочный тест Docker для встроенного выпуска | +|Команда|Цель| +| --- | --- | +| `make generate_deleted_files` |Регенерировать `src/migrations/deleted_files.txt`| +| `make new` |Wipe + recreate `dist/` — запускается ОДИН раз в начале (шаг 1), перед записью `dist/changes.md`; никогда больше перед созданием| +| `make lb` |Создайте архив LoadBalancer в виде `dist/`| +| `make main` |Соберите ОСНОВНОЙ архив в `dist/`| +| `bash tools/test-install/test_release.sh` |Установочный тест Docker для встроенного выпуска| **Документация** (английский источник в `docs/en`; `docs/ru` сгенерирован + зафиксирован): -| Команда | Цель | -| --------------------- | ----------------------------------------------------------------------------------------- | -| `make docs-venv` | Одноразовый: локальный venv (сборка + переводы) | -| `make docs-translate` | Восстановить `docs/ru` из `docs/en` (перед выпуском) | -| `make docs-build` | Строгая сборка MkDocs в `./build/site` (что запускает CI) | -| `make docs-serve` | Предварительный просмотр документов в режиме реального времени на `http://127.0.0.1:8000` | +|Команда|Цель| +| --- | --- | +| `make docs-venv` |Одноразовый: локальный venv (сборка + переводы)| +| `make docs-translate` |Восстановить `docs/ru` из `docs/en` (перед выпуском)| +| `make docs-build` |Строгая сборка MkDocs в `./build/site` (что запускает CI)| +| `make docs-serve` |Предварительный просмотр документов в режиме реального времени на `http://127.0.0.1:8000`| diff --git a/docs/ru/development/streaming-subsystem.md b/docs/ru/development/streaming-subsystem.md index d4f15065..4f4e773d 100644 --- a/docs/ru/development/streaming-subsystem.md +++ b/docs/ru/development/streaming-subsystem.md @@ -189,11 +189,11 @@ Alt-Svc: h3-29, h3-T051, h3-Q050 (HTTP/3 hints) ### VOD (vod.php) -Тот же процесс аутентификации, что и в live. Считывается из `VOD_PATH` вместо `STREAMS_PATH`. +Тот же процесс аутентификации, что и в live. Считывается из `VOD_PATH` вместо `STREAMS_PATH`. Диапазоны байтов (поиск) разрешаются с помощью `Streaming\Delivery\HttpRange` (одиночные диапазоны RFC 7233, включая диапазоны суффиксов). Видеофильм с прямым подключением передается с помощью cURL, запрашивая у источника точно запрошенный диапазон. ### Временной сдвиг (timeshift.php) -Обслуживает архивированные сегменты (timeshift / catch-up) из пути к архиву. +Обслуживает архивированные сегменты (timeshift / catch-up) из пути к архиву. Запрос TS передает в потоковом режиме несколько файлов подряд; им сопоставляется диапазон байт (поиск) — файлы перед началом пропускаются, первый вводится с правильным смещением, и доставка прекращается в конце диапазона. ### Доставка демона — `xc_fanout` @@ -203,23 +203,93 @@ PHP-FPM работник, отвечающий за жизнедеятельно - **Расходимся веером.** `xc_fanout` (встроенный демон Go) извлекает каждый источник **однажды** и предоставляет его каждому пользователю через сокет unix с помощью встроенного в оперативную память сегментатора HLS. -PHP находится вне байтового пути для каждого зрителя: рабочий процесс чтения для каждого зрителя -цикл обслуживания и путь `HLSGenerator::generateHLS()` для обслуживания клиентов не являются -больше не используется для оперативной доставки (`generateHLS()` сохраняется в классе, но имеет -абонентов нет). `AsyncFileOperations::awaitFileExists()` — это **нет** удалено - это -по-прежнему используется для ожидания запуска потока и пути в байтах VOD/timeshift (см. -Таблица показателей). +PHP не соответствует байтовому пути для каждого зрителя: рабочий процесс чтения для каждого зрителя +цикл обслуживания и путь к клиенту на диске `generateHLS()` исчезли. +`AsyncFileOperations::awaitFileExists()` по-прежнему используется для запуска потока +ожидания и путь в байтах VOD/timeshift (см. таблицу производительности). +- **Кто кормит демона.** Поскольку демон является единственным путем к клиенту, каждый живой +продюсер должен включить его, иначе канал нельзя будет смотреть: +поток ffmpeg переходит в свой принимающий сокет (`buildLive()`; обратная петля +включая дочерних), создатели супервизора-демона делают то же самое, PHP +производители — сегментатор LLOD (`LlodCommand`) и ретранслятор с обратной связью +(`LoopbackCommand`) — протолкнуть через `Streaming\Fanout\IngestFeeder`, и +поток **отложенный** подается через `DelayCommand`, который перемещает каждый задержанный сегмент +по мере того, как он публикует его, в зависимости от продолжительности сегмента (его выходные данные с кодировщика являются +отложенный, поэтому тройник для него не используется). `IngestFeeder` буферизует то, что +не удалось отправить неблокирующую запись (короткая запись больше не разрывает пакеты), +выполняет повторную регистрацию и набор номера после перезапуска демона и содержит ключ HLS. - **Две розетки.** Клиентский сокет (ориентированный на nginx) обслуживает `/live/` и `/hls/...`; управляющий сокет, предназначенный только для PHP, регистрирует источники (`PUT /streams/` / `/ingest/`), отвечает на вопросы о статусе выхода в эфир (`GET /streams/`, `GET /probe/`) и предоставляет доступ к телеметрии. - **Телеметрия / согласование данных.** `fanout_sync` опросы `GET /rates` (для каждого uuid -КБИТ/с → `lines_divergence`) и `GET /connections` (согласовывает `lines_live` -строк, поскольку PHP не может видеть разъединение в `X-Accel`). +КБИТ/с → `lines_divergence`) и сверяет `GET /connections` с данными +`lines_live` строк в обоих направлениях: строка, просмотрщик которой покинул демон, называется +закрыто (PHP не удается увидеть отключение при `X-Accel`), и демон-просмотрщик, чей +пропущенная строка — собранная, с истекшим сроком действия или запрещенная строка — удаляется по истечении 20 секунд +(`DELETE /connections/`). +- **Удары и ограничения по подключению.** Строка TS viewer, обслуживаемая демоном, содержит `pid = 0`: +здесь нет рабочего, которого можно было бы убить. `ConnectionLimiter` / `ConnectionTracker::closeConnection()` +завершите его с помощью `ConnectionTracker::dropDaemonViewer()` — `FanoutClient::dropConnection()` +на этом узле, или сигнал `drop_con` о том, что узел зрителя превращается в +тот же вызов. Ограничитель никогда не отключает запрашивающее соединение сам по себе (это +идентифицируется по uuid, поскольку каждая строка демона имеет общий pid 0). - **Вне эфира.** Если демон сообщает об отсутствии данных (`has_data=false` / устаревшие), PHP показывает страницу "не в эфире" вместо того, чтобы позволить зрителю зависнуть. - **Сохранено на диске HLS** только для timeshift / миниатюр / `.analyse` / -`MonitorCommand` — не для доставки клиенту. +дочерние элементы loopback / проверки запуска по требованию — не для доставки клиенту. + +#### Управление потоковой передачей и встроенный ремультиплексор + +При включенном **Контроль разветвленного энкодера** (`fanout_supervise`, миграция 018, по умолчанию включено), +прямая трансляция не получает PHP watchdog. `StreamProcess::startMonitor()` строит свои команды и раздает +их супервизору демона (`FanoutClient::supervise` → `PUT /monitor/`), который запускает, +отслеживает и перезапускает их — отработка отказа, приоритетное резервное копирование, принудительный источник, сбой вывода, потеря звука, +включая снижение частоты кадров и перезапуск по расписанию. PHP продолжает создавать каждую команду и выполнять все +запись в базу данных; демон выполняет то, что ему передают. + +- **Передача** — `StreamProcess::superviseStream()` сначала запрашивает у демона +(`GET /monitors/state`: доступно, `accepting`), создает спецификацию +(`StreamProcess::buildSupervisorSpec()`: одна команда для каждого источника, политики и работоспособности, сопоставленных с +соблюдены настройки `MonitorCommand`), записывает pid демона как `monitor_pid`, затем передает его + over. Without a restart a running encoder is **adopted**, not replaced; `cron:streams` moves +PHP-отслеживает потоки таким образом при следующем проходе. +- **Команды** — прямая трансляция, доступная только для копирования, запускает собственный ремуксор демона, `xc_fanout remux`, +построенный с помощью `StreamProcess::buildNativeLive()` рядом с `buildLive()`: он считывает исходный код изначально +(MPEG-TS по протоколу http(s), HLS с сегментами TS, udp/rtp) и записывает то же самое на диск HLS и демон +подача в виде строки ffmpeg `-f tee` без ffmpeg. Какие потоки соответствуют требованиям, это +`StreamProcess::nativeRefusal()` / `isNativeSource()`; `fanout_source_backend` решает: +`auto` = повторный мультиплексор с командой ffmpeg в виде `fallback_cmd` (используется при выходе из режима 3, +"не удается обслуживать этот источник"), `native` = только для ремультиплексора, `ffmpeg` = только для ffmpeg. Только панель +записывает команду ремультиплексора, когда демон узла объявляет об этом (`features` в +`GET /monitors/state`, `FanoutClient::supportsRemux()`) — более старый двоичный файл неправильно разобрал бы его. +- **Какой продюсер баллотировался и почему** — переданная команда записывается рядом с записью потока. +файлы, подобные пути к самопроизвольному запуску `_.ffmpeg`: `_.fanout` для ремультиплексора, +`_.ffmpeg` для ffmpeg (в `auto` - оба). Когда включен собственный сервер и выполняется поток +ffmpeg в любом случае, причина `StreamProcess::nativeRefusal()` добавляется к `.errors` +(`[panel] ffmpeg runs this stream: transcoding is enabled`), тот же файл, что и у производителя. +stderr переходит в. Соответствующий тип `streams_types.type_key` = `live`; `gen_timestamps` и +`read_native` намеренно не являются отказами (оба значения по умолчанию равны 1, поэтому они ничего не говорят о +канал — смотрите программу запуска демона). +- **Reconcile** — the daemon cannot write the database, so `StreamProcess::reconcileSupervised()` +копирует его состояние в `streams_servers` (статус, pid, текущий источник, кодеки, разрешение, +измеренный битрейт): каждый проход `cron:streams` и каждые 5 секунд от демона `signals`. A +контролируемый поток, строка которого пропущена или помечена как остановленная, освобождается. Кодеки и размер изображения +записываются в `stream_info` JSON, а также в плоские столбцы — этот JSON - это то, что +отображает список потоков, из которого адаптивный мастер-плейлист берет `BANDWIDTH`/`RESOLUTION` и +где `stream/auth.php` считывает видеокодек зрителя, и контролируемый поток никогда не запускается +ffprobe чтобы заполнить его. +- **Остановка** — `StreamProcess::stopStream()` освобождает первый (`DELETE /monitor/`, который убивает +продюсер); сначала супервизор перезапускает процесс, убивая продюсера. +- **Возврат к PHP** — демон, который отключен или не принимает, и поток указывает, что он не принимает +берем (задержка, созданные каналы, `yt-dlp` исходники платформы), запускаем `MonitorCommand`, как и раньше; +`MonitorCommand` отключается для потока, который контролирует демон. +- **"За этим следят?"** — для контролируемого потока `monitor_pid` - это pid демона, поэтому вызывающие абоненты используют +`StreamProcess::isWatched()` (PHP монитор активен или находится под наблюдением), а не +`ProcessManager::isMonitorAlive()` один. + +Runbook на стороне демона - включение, проверка, откат кодов завершения работы ремультиплексора — это +`docs/en/09-encoder-supervision.md` в репозитории `XC_VM_Fanout`. #### Наложение отправленного сообщения @@ -349,7 +419,11 @@ IP-блокировка на основе файлов. Файлы блоков ### 3. Ограничитель подключений (для каждого пользователя) -Применяется после проверки токена. Ограничивает одновременные потоки для каждого пользователя на основе `max_connections`. +Применяется после проверки токена. Ограничивает одновременные потоки для каждого пользователя на основе `max_connections`, закрывая сначала самые старые соединения (более старые соединения самого запрашивающего устройства перед другими). Средства просмотра, обслуживаемые демонами, отключаются с помощью демона — см. [Daemon delivery](#daemon-delivery-xc_fanout). + +### 4. Серверы, работающие только через прокси + +Сервер с параметром `enable_proxy` принимает только запросы, поступающие через один из его прокси-серверов. `auth.php` проверяет одноранговый узел TCP nginx saw — `XC_PEER_ADDR`, значение которого равно `$realip_remote_addr` в расположении потока `nginx.conf`, а не заголовок запроса, которым управляет клиент. --- @@ -357,12 +431,12 @@ IP-блокировка на основе файлов. Файлы блоков Клиент HLS обслуживается демоном `xc_fanout` (см. [Доставка демоном](#daemon-delivery-xc_fanout)), поэтому происходит шифрование **сторона демона**: -1. `StreamProcess` записывает ключ AES-128 потока/IV в `content/streams/_.key` / `_.iv`. -2. At ingest registration (`FanoutClient::registerIngest`), when `encrypt_hls` is on, the key/IV are handed to the daemon, which encrypts the HLS segments it serves and emits a matching `#EXT-X-KEY`. -3. `HLSGenerator::tokenizeDaemonPlaylist()` переписывает URL-адреса сегментов плейлиста демона в ссылки с авторизацией для каждого сегмента `/hls/`, которые `segment.php` передаются через прокси-сервер демона. +1. `StreamProcess` записывает ключ AES-128 потока/IV в `content/streams/_.key` / `_.iv` — перед тем, как он создаст PHP производителя, который регистрируется демоном через несколько мгновений после запуска. +2. При регистрации приема (`FanoutClient::registerIngest`), когда включено `encrypt_hls`, ключ/IV передается демону, который шифрует HLS сегмента, которые он обслуживает. Каждый производитель передает их — ffmpeg потоков (включая дочерние циклы), контролируемые потоки и PHP производителей через `IngestFeeder::forStream()` — потому что плейлист всегда объявляет ключ: демон, запущенный без него, подавал простые сегменты, которые ни один игрок не смог бы расшифровать. +3. `HLSGenerator::tokenizeDaemonPlaylist()` переписывает URL-адреса сегментов плейлиста демона в ссылки с авторизацией для каждого сегмента `/hls/`, которые `segment.php` проксируются от демона, и добавляет строку `#EXT-X-KEY`. 4. Ключ AES доставляется игрокам с помощью `key.php` (`src/Public/stream/key.php`) с использованием того же механизма токенов. -> Устаревший `HLSGenerator::generateHLS()` (который создал и зашифровал плейлист на диске HLS для использования PHP) сохраняется в классе, но становится **больше не находится на пути к клиенту** после отключения демона. +Значение `#EXT-X-MEDIA-SEQUENCE` в плейлисте live повторно привязывается к значению `HlsSequence`, поэтому он никогда не возвращается назад при переходе из прямого эфира в прямой эфир без изменения нумерации воспроизводимого потока (его состояние сохраняется в виде `tmp/signals/hlsseq_`, поэтому оно сохраняется после перезапуска потока). --- @@ -431,5 +505,7 @@ SIGNALS_PATH = /home/xc_vm/signals/ | `src/Streaming/AsyncFileOperations.php` |неблокирующие утилиты для файловой системы| | `src/Streaming/Lifecycle/ShutdownHandler.php` |очистка соединения при выходе| | `src/Domain/Stream/ConnectionTracker.php` |состояние соединения в Redis/MySQL| +| `src/Domain/Stream/StreamProcess.php` |формирование команды (`buildLive` / `buildNativeLive`), передача контроля и согласование| +| `src/Streaming/Fanout/FanoutClient.php` |API управления демонами (прием, контроль, принудительный источник)| | `src/Core/Init/LegacyInitializer.php` |настройка глобальной переменной для потоковой передачи| | `tools/stream-check/stream_check.py` |проверка целостности очереди + пакет плейлистов + панель мониторинга живого буфера + графический редактор SVG| diff --git a/docs/ru/guides/cli-tools.md b/docs/ru/guides/cli-tools.md index acacf7a7..2425c592 100644 --- a/docs/ru/guides/cli-tools.md +++ b/docs/ru/guides/cli-tools.md @@ -46,7 +46,7 @@ | `xcvm_core` | `XcvmCoreCommand` |Установите/обновите расширение `xcvm_core` PHP из хранилища двоичных файлов|корень| | `ytdlp` | `YtDlpCommand` |Установите/обновите `yt-dlp` из своей предыдущей версии на GitHub|корень| | `startup` | `StartupCommand` |Инициализация системы: daemons.sh, crontab, кэш|корень| -| `monitor` | `MonitorCommand` |Отслеживайте поток по идентификатору (запуск/перезапуск/отслеживание)|xc_vm| +| `monitor` | `MonitorCommand` |Отслеживайте поток по идентификатору (start/restart/track). Только для потоков, которые не выполняет супервизор xc_fanout — он заменяет контролируемый поток|xc_vm| | `thumbnail` | `ThumbnailCommand` |Создание рамок миниатюр для потока|xc_vm| | `plex_item` | `PlexItemCommand` |Обработать один элемент Plex (фильм/сериал)|xc_vm| | `watch_item` | `WatchItemCommand` |Обработать один элемент наблюдения (поиск/обновление в базе данных TMDB)|xc_vm| @@ -127,7 +127,7 @@ | `cron:plex` | `PlexCronJob` |сплетение|Обрабатывать обновления Plex| | `cron:watch` | `WatchCronJob` |часы|Обрабатывать обновления библиотеки отслеживания| -> "Необязательные" задания cron регистрируются **нет** условно — регистрируется каждый обнаруженный класс `CronJob`. "Необязательно" означает, что задание не выполняется, если не включена его функция/настройка (например, `cron:epg`, `cron:series`, `cron:update`), или если задание не удалено из сборки LB. +> "Необязательные" задания cron регистрируются **нет** условно — регистрируется каждый обнаруженный `CronJob` класс. "Необязательно" означает, что задание не выполняется, если не включена его функция/настройка (например, `cron:epg`, `cron:series`, `cron:update`), или если задание не удалено из сборки LB. --- @@ -138,12 +138,12 @@ старая сборка, никогда не сходилась бы). `cron:root_signals` (root, каждую минуту) сохраняет они становятся текущими путем опроса их идемпотентных команд обновления для каждого двоичного файла на расписание с ограниченным использованием штампов - каждая загрузка выполняется только при несоответствии версии, проверяется -контрольная сумма, запуск-тестирует новый двоичный файл, затем заменяет его атомарно (неработающая загрузка +контрольная сумма, запуск-тестирует новый двоичный файл, затем атомарно заменяет его (неработающая загрузка никогда не заменяет рабочий). Выполняется на каждом узле (main **и** LB). |Двоичный|Команда|Источник|Проверить|Опрос| | --- | --- | --- | --- | --- | -|`xc_fanout` демон| `fanout_binary` |`XC_VM_Fanout` освободить актив| `SHA256SUMS` |~ежечасно| +|`xc_fanout` демон| `fanout_binary` |`XC_VM_Fanout` высвободить актив| `SHA256SUMS` |~ежечасно| |`xcvm_core` расширение| `xcvm_core` |`XC_VM_Binaries` дерево репозиториев (`bin/xcvm_core/`)|`SHA256SUMS` + нагрузочный тест|~ежечасно| | `yt-dlp` | `ytdlp` |вышестоящий `yt-dlp/yt-dlp` релиз|`SHA2-256SUMS` + `--version`|ежедневный| @@ -321,7 +321,7 @@ su - xc_vm -c '/home/xc_vm/console.php tools bouquets' ## Обновления / миграции баз данных -Файловая система обновления базы данных (создание шага `.sql`, таблицы `migrations`, потока выполнения `db:migrate`) теперь доступна на отдельной странице — см. [Обновления / миграции баз данных](database-migrations.md). +Файловая система обновления базы данных (создание шага `.sql`, таблицы `migrations`, потока выполнения `db:migrate`) теперь размещена на отдельной странице - см. [Обновления / миграции баз данных](database-migrations.md). --- @@ -333,7 +333,7 @@ su - xc_vm -c '/home/xc_vm/console.php tools bouquets' sudo /home/xc_vm/console.php status ``` -Проверяет, запущен ли XC_VM, подключается к базе данных, выполняет ожидающие обновления шаги, исправляет разрешения и проверяет конфигурацию nginx. Требуется после установки или восстановления. +Проверяет, запущен ли параметр XC_VM, подключается к базе данных, выполняет ожидающие обновления шаги, исправляет разрешения и проверяет конфигурацию nginx. Требуется после установки или восстановления. С аргументом `first-run` пропускает текущую проверку, используемую для начальной настройки: @@ -373,7 +373,7 @@ sudo /home/xc_vm/console.php server:diagnose sudo /home/xc_vm/console.php server:diagnose ``` -Обнаруживает **почему?** прокси—сервер/LB—узел, отображаемый в автономном режиме на панели: проверяет частоту сердечных сокращений, доступность (ICMP/TCP/HTTP `/api`), перекос часов, очередь сигналов и - локально на узле - выполняет ли узел брандмауэр на главном IP-адресе в своем собственном iptables, запущена ли служба `xc_vm`/nginx, запущен ли демон сердцебиения `watchdog` и находится ли `cron:servers` в crontab `xc_vm`. Доступно только для чтения; код выхода `0` = проблем не обнаружено, `2` = указаны вероятные причины. Более подробную информацию смотрите в [Руководстве по диагностике сервера](../administration/server-diagnostics.md). +Обнаруживает **почему?** прокси—сервер/LB—узел, отображаемый в автономном режиме на панели: проверяет частоту сердечных сокращений, доступность (ICMP/TCP/HTTP `/api`), смещение тактовых импульсов, очередь сигналов и - локально на узле - выполняет ли узел брандмауэр на главном IP-адресе в своем собственном iptables, запущена ли служба `xc_vm`/nginx, запущен ли демон сердцебиения `watchdog` и находится ли `cron:servers` в crontab `xc_vm`. Доступно только для чтения; код завершения `0` = проблем не обнаружено, `2` = указаны вероятные причины. Подробности см. в [Руководстве по диагностике сервера](../administration/server-diagnostics.md). ### SSL-сертификат diff --git a/docs/ru/guides/feature-flags.md b/docs/ru/guides/feature-flags.md index 6eac363e..58acb7d8 100644 --- a/docs/ru/guides/feature-flags.md +++ b/docs/ru/guides/feature-flags.md @@ -58,6 +58,8 @@ define('DEV_MODE', false); // master development-mode flag | `recaptcha_enable` | `bool` |включите reCAPTCHA v2 при входе в систему| | `verify_host` | `bool` |принудительная проверка списка разрешений хоста| | `save_login_logs` | `bool` |постоянные попытки входа в систему в `login_logs`| +| `fanout_supervise` | `bool` |передача прямых трансляций супервизору xc_fanout вместо монитора PHP (по умолчанию включен)| +| `fanout_source_backend` |`auto` / `ffmpeg` / `native`|как преобразуются исходные тексты в MPEG-TS; под наблюдением, будут ли потоки, предназначенные только для копирования, запускать собственный ремиксер (`auto`: с резервным копированием ffmpeg)| Эти значения загружаются из `CACHE_TMP_PATH/settings` защитниками запросов. diff --git a/docs/ru/guides/process-management.md b/docs/ru/guides/process-management.md index bed00e32..1293229f 100644 --- a/docs/ru/guides/process-management.md +++ b/docs/ru/guides/process-management.md @@ -36,8 +36,14 @@ ProcessManager::isStreamRunning(int $pid, int $streamId): bool ``` - `ffmpeg`: проверяет шаблон вывода для конкретного потока в командной строке +- `xc_fanout`: только собственный ремультиплексор (`xc_fanout remux … /_.m3u8`) — демонический процесс +сам по себе использует исполняемый файл, но не называет список воспроизведения потока - `php`: считается активным для контекста stream worker +Для запроса "есть ли что-нибудь **наблюдающий** в этом потоке" используйте `StreamProcess::isWatched($streamId, $monitorPid)`: +`monitor_pid` контролируемого потока принадлежит демону xc_fanout, который `isMonitorAlive()` (an +`XC_VM[]` PHP процесс) справедливо отклоняется. + --- ## Больше проверок и помощников @@ -61,6 +67,28 @@ ProcessManager::isAnyProcessRunning(array $terms): bool --- +## Процессор и память для каждого потока + +```php +ProcessManager::resourceSample($pid): ?array // ['ticks' => CPU ticks so far, 'rss' => bytes, 'at' => microtime] +ProcessManager::cpuPercent(array $now, array $prev): ?float // percent of ONE core between two samples +ProcessManager::producerKind($pid): ?string // 'fanout' (xc_fanout remux) | 'ffmpeg' | 'php' +``` + +Считывается непосредственно из `/proc/PID/stat` (поля 14/15 для CPU, 24 для RSS; размер страницы является производным +из собственного значения этого процесса `statm` против `status`, поскольку 64 КБ страниц являются нормальными для arm64). Процессор в `/proc` +is cumulative, so a percentage needs **two** samples: `cpuPercent()` returns `null` when the pair +ничего не говорит — ни о предыдущих показаниях, ни о двух показаниях за один и тот же момент, ни о счетчике, который сработал. +в обратном направлении, потому что производитель перезапустился в том же потоке. + +`cron:streams` производит выборку каждого запущенного потока один раз за проход и сводит результат к этому +JSON-файл потока `progress_info` (`cpu`, `mem`, `producer`, плюс `cpu_t` / `cpu_at`, несущие +чтение следующего прохода вычитается из). Только узел, на котором запущен поток, может считывать свой собственный `/proc`, поэтому +выборка происходит там и перемещается на панель в строке, которую cron уже записывает; администратор +список потоков отображает его в виде столбца **Ресурсы** (значок производителя, процент процессора, оперативная память). + +--- + ## Завершение процесса ```php @@ -93,10 +121,10 @@ ProcessManager::acquireCronLock(string $pidFile, int $maxAge = 1800): bool ## `/proc` Проверить кэш -`isRunning()` использует короткий TTL-кэш для `/proc` проверок (1 секунда), чтобы уменьшить количество повторных операций ввода-вывода в узких циклах. +`isRunning()` использует короткий TTL-кэш для `/proc` проверок (1 секунда), чтобы уменьшить количество повторяющихся операций ввода-вывода в узких циклах. > **Ловушка.** Поскольку результат кэшируется в течение ~1 секунды, процесс, который завершается (или запускается) внутри этого -> окно по—прежнему считывается в своем предыдущем состоянии - жесткий цикл может воздействовать на устаревшее "запущенное"/ "мертвое" окно. +> "окно" по—прежнему считывается в своем предыдущем состоянии - жесткий цикл может воздействовать на устаревшее "запущенное"/ "мертвое" окно. > ответ. Вызовите `ProcessManager::clearCache()`, чтобы удалить кэш, когда вам понадобится новое чтение > (например, сразу после удаления pid и перед его повторной проверкой). @@ -122,7 +150,7 @@ ProcessManager::acquireCronLock(string $pidFile, int $maxAge = 1800): bool - `Thread` (`src/Core/Process/Thread.php`) — тонкая оболочка `proc_open` вокруг одного фоновая команда (запустите ее, опросите/дождитесь ее, прочитайте ее выходные данные). -- `Multithread` (`src/Core/Process/Multithread.php`) — выполняет несколько команд оболочки. +- `Multithread` (`src/Core/Process/Multithread.php`) — запускает несколько команд оболочки. одновременно и собирает выходные данные каждого из них; используйте его для разветвленной работы (например, для проверки многих исходники сразу), а не ручной цикл `proc_open`. diff --git a/src/Core/Config/AppConfig.php b/src/Core/Config/AppConfig.php index 76da4c0d..c4d70659 100644 --- a/src/Core/Config/AppConfig.php +++ b/src/Core/Config/AppConfig.php @@ -30,7 +30,7 @@ define('DEV_MODE', false); // ── Version & Git Configuration ──────────────────────────────── -define('XC_VM_VERSION', '2.5.0'); +define('XC_VM_VERSION', '2.5.1'); define('GIT_OWNER', 'Vateron-Media'); define('GIT_REPO_MAIN', 'XC_VM');