diff --git a/docs/en/development/cli-tools.md b/docs/en/development/cli-tools.md index c1fa74ae..c061c0e8 100644 --- a/docs/en/development/cli-tools.md +++ b/docs/en/development/cli-tools.md @@ -1,6 +1,6 @@ -# 🛠 CLI Tools & Database Migrations +# 🛠 CLI Tools & Database Updates -Reference for XC_VM command-line interface, system tools, and the database migration system. Covers daily operations, emergency access, and creating new DB migrations. +Reference for XC_VM command-line interface, system tools, and the database update process after version upgrades. Covers daily operations, emergency access, and creating new DB update steps. --- @@ -10,8 +10,8 @@ Reference for XC_VM command-line interface, system tools, and the database migra - [📋 Full Command Registry](#full-command-registry) - [🆕 Registering a New Command](#registering-a-new-command) - [🔧 Tools Command](#tools-command) -- [🗃 Database Migrations](#database-migrations) -- [📝 Creating a New Migration](#creating-a-new-migration) +- [🗃 Database Updates After Version Upgrade](#database-updates-after-version-upgrade) +- [📝 Creating a New DB Update Step](#creating-a-new-db-update-step) - [⚙️ Common CLI Operations](#common-cli-operations) --- @@ -48,7 +48,7 @@ To see all available commands: | Command | Class | Description | User | | --- | --- | --- | --- | -| `status` | `StatusCommand` | System status, DB migrations, configuration check | root | +| `status` | `StatusCommand` | System status, DB updates, configuration check | root | | `update` | `UpdateCommand` | System update (update / post-update) | xc_vm | | `service` | `ServiceCommand` | Manage XC_VM service: start, stop, restart, reload | root | | `tools` | `ToolsCommand` | Maintenance utilities (see Tools Command section) | root/xc_vm | @@ -59,7 +59,7 @@ To see all available commands: | `thumbnail` | `ThumbnailCommand` | Generate thumbnail frames for a stream | xc_vm | | `plex_item` | `PlexItemCommand` | Process single Plex item (movie/series) | xc_vm | | `watch_item` | `WatchItemCommand` | Process single Watch item (TMDB search/update) | xc_vm | -| `migrate` | `MigrateCommand` | Migrate data from `xc_vm_migrate` database | xc_vm | +| `migrate` | `MigrateCommand` | Transfer data from `xc_vm_migrate` database | xc_vm | | `balancer` | `BalancerCommand` | Install/configure load balancer via SSH | root | > Commands marked **optional** are conditionally registered via `file_exists()` guard: `cache_handler`, `balancer`, `migrate`. @@ -231,7 +231,7 @@ The `tools` command provides system maintenance utilities. | `rescue` | Create a temporary rescue access code for emergency panel access. Prints the URL. **Delete this code after use!** | | `access` | Regenerate all nginx access code configs and reload nginx. Prints URLs for all admin panel codes. | | `ports` | Regenerate nginx port configs (HTTP, HTTPS, RTMP) from the database and reload nginx. | -| `migration` | Clear the migration database (`xc_vm_migrate`) and optionally restore a `.sql` backup into it. | +| `migration` | Clear the staging database (`xc_vm_migrate`) and optionally restore a `.sql` backup into it. | | `user` | Create a rescue admin user with random credentials. Prints username and password. **Delete this user after use!** | | `mysql` | Reauthorise MySQL privileges for all load balancer servers. | | `database` | Restore a blank XC_VM database from `database.sql`. **Erases ALL data!** Requires `--confirm` flag. | @@ -257,10 +257,10 @@ sudo /home/xc_vm/console.php tools access # Regenerate port configuration (root) sudo /home/xc_vm/console.php tools ports -# Clear migration database (root) +# Clear staging database (root) sudo /home/xc_vm/console.php tools migration -# Clear migration database and restore a backup (root) +# Clear staging database and restore a backup (root) sudo /home/xc_vm/console.php tools migration /path/to/backup.sql # Create rescue admin user (root) @@ -292,13 +292,13 @@ su - xc_vm -c '/home/xc_vm/console.php tools bouquets' --- -## Database Migrations +## Database Updates After Version Upgrade -XC_VM uses a file-based migration system to manage database schema changes between versions. Migrations are executed automatically during updates and system status checks. +XC_VM uses a file-based DB update system to manage schema changes between versions. DB updates are executed automatically during updates and system status checks. ### How It Works -- Migration files are stored as `.sql` files in `/home/xc_vm/migrations/`. +- SQL files for DB updates are stored in `/home/xc_vm/migrations/`. - Each file is named with a sequential number prefix, for example: @@ -308,16 +308,16 @@ XC_VM uses a file-based migration system to manage database schema changes betwe 003_drop_settings_segment_type.sql ``` -- Applied migrations are tracked in the `migrations` database table. Each migration runs **exactly once** - if a migration has already been applied, it is skipped. +- Applied DB update steps are tracked in the `migrations` database table. Each step runs **exactly once** - if a step has already been applied, it is skipped. -- Migrations are executed automatically by: +- DB updates are executed automatically by: - `console.php update post-update` - after a panel update - `console.php status` - during system status check (MAIN server only) -### Migration Execution Flow +### DB Update Execution Flow ```text -[ MigrationRunner::run() ] +[ MigrationRunner::run() — DB update execution ] │ ▼ [ CREATE TABLE IF NOT EXISTS `migrations` ] @@ -334,9 +334,9 @@ XC_VM uses a file-based migration system to manage database schema changes betwe --- -## Creating a New Migration +## Creating a New DB Update Step -When you need to modify the database schema (add columns, create tables, insert data, etc.), create a new SQL migration file. +When you need to modify the database schema (add columns, create tables, insert data, etc.), create a new SQL file for a DB update step. ### Step 1. Choose a File Name @@ -350,7 +350,7 @@ NNN_short_description.sql - Number prefix: 3 digits, zero-padded (e.g., `006`, `007`) - Separator: underscore `_` -- Name: lowercase, underscores, describing what the migration does +- Name: lowercase, underscores, describing what the update step does - Extension: `.sql` **Examples:** @@ -365,9 +365,9 @@ NNN_short_description.sql Place raw SQL statements in the file. Multiple statements are separated by `;`. -**Rules for migration SQL:** +**Rules for SQL DB update steps:** -- **Use `IF EXISTS` / `IF NOT EXISTS`** to make migrations idempotent: +- **Use `IF EXISTS` / `IF NOT EXISTS`** to make DB update steps idempotent: ```sql -- Adding a column (safe) @@ -393,13 +393,13 @@ FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `streams_arguments` WHERE argument_key = 'my_key'); ``` -- **Do not mix DDL and DML** that depend on each other in the same file. If you need to add a column and then populate it, use two migration files. +- **Do not mix DDL and DML** that depend on each other in the same file. If you need to add a column and then populate it, use two DB update step files. - **Comments** are supported with `--` prefix (they are skipped during execution). ### Step 3. Place the File -Copy the migration file to: +Copy the SQL file for the DB update step to: ```text /home/xc_vm/migrations/ @@ -407,9 +407,9 @@ Copy the migration file to: > 💡 In the source repository, this is `src/migrations/`. -### Step 4. Validate Migration +### Step 4. Validate DB Update -Run the status command to apply pending migrations: +Run the status command to apply pending DB update steps: ```bash sudo /home/xc_vm/console.php status first-run @@ -424,7 +424,7 @@ Migrations ``` -If a statement fails, the migration will still be recorded but show `[WARN]` — review the SQL and fix any issues. +If a statement fails, the step will still be recorded but show `[WARN]` — review the SQL and fix any issues. --- @@ -436,7 +436,7 @@ If a statement fails, the migration will still be recorded but show `[WARN]` — sudo /home/xc_vm/console.php status ``` -Checks if XC_VM is running, connects to the database, runs pending migrations, fixes permissions, and validates nginx configuration. Required after installation or recovery. +Checks if XC_VM is running, connects to the database, runs pending DB update steps, fixes permissions, and validates nginx configuration. Required after installation or recovery. With `first-run` argument, skips the running check — used for initial setup: @@ -472,12 +472,12 @@ Starts a stream manually and displays any errors. Useful for diagnosing stream s sudo /home/xc_vm/console.php certbot ``` -### Migration (from Other Systems) +### Database Update with Data from Other Systems ```bash /home/xc_vm/console.php migrate ``` -Transfers data from a migration database. See the [Migration Guide](en-us/info/migration_guide.md) for details. +Transfers data from the staging database `xc_vm_migrate`. See the [Database Update Guide](en-us/info/migration_guide.md) for details. --- diff --git a/docs/ru/development/cli-tools.md b/docs/ru/development/cli-tools.md index 25133dc2..5289a532 100644 --- a/docs/ru/development/cli-tools.md +++ b/docs/ru/development/cli-tools.md @@ -1,6 +1,6 @@ -# 🛠 CLI-инструменты и миграции БД +# 🛠 CLI-инструменты и обновление БД -Справочник по командной строке XC_VM, системным утилитам и системе миграций базы данных. Покрывает повседневные операции, экстренный доступ и создание новых миграций БД. +Справочник по командной строке XC_VM, системным утилитам и процессу обновления базы данных после обновления версии. Покрывает повседневные операции, экстренный доступ и создание новых шагов обновления БД. --- @@ -10,8 +10,8 @@ - [📋 Полный реестр команд](#полный-реестр-команд) - [🆕 Регистрация новой команды](#регистрация-новой-команды) - [🔧 Команда tools](#команда-tools) -- [🗃 Миграции базы данных](#миграции-базы-данных) -- [📝 Создание новой миграции](#создание-новой-миграции) +- [🗃 Обновление БД после обновления версии](#обновление-бд-после-обновления-версии) +- [📝 Создание нового шага обновления БД](#создание-нового-шага-обновления-бд) - [⚙️ Типичные CLI-операции](#типичные-cli-операции) --- @@ -48,7 +48,7 @@ | Команда | Класс | Описание | Пользователь | | --- | --- | --- | --- | -| `status` | `StatusCommand` | Статус системы, миграции БД, проверка конфигурации | root | +| `status` | `StatusCommand` | Статус системы, обновление БД, проверка конфигурации | root | | `update` | `UpdateCommand` | Обновление системы (update / post-update) | xc_vm | | `service` | `ServiceCommand` | Управление сервисом XC_VM: start, stop, restart, reload | root | | `tools` | `ToolsCommand` | Утилиты обслуживания (см. раздел "Команда tools") | root/xc_vm | @@ -59,7 +59,7 @@ | `thumbnail` | `ThumbnailCommand` | Генерация превью-кадров для стрима | xc_vm | | `plex_item` | `PlexItemCommand` | Обработка элемента Plex (фильм/сериал) | xc_vm | | `watch_item` | `WatchItemCommand` | Обработка элемента Watch (поиск/обновление TMDB) | xc_vm | -| `migrate` | `MigrateCommand` | Миграция данных из БД `xc_vm_migrate` | xc_vm | +| `migrate` | `MigrateCommand` | Перенос данных из БД `xc_vm_migrate` | xc_vm | | `balancer` | `BalancerCommand` | Установка/настройка балансировщика по SSH | root | > Опциональные команды (условная регистрация через `file_exists()`): `cache_handler`, `balancer`, `migrate`. @@ -231,7 +231,7 @@ console.php tools | `rescue` | Создать временный код доступа для экстренного входа в панель. Выводит URL. **Удалите код после использования!** | | `access` | Перегенерировать все конфигурации кодов доступа nginx и перезагрузить nginx. Выводит URL всех кодов админ-панели. | | `ports` | Перегенерировать конфигурации портов nginx (HTTP, HTTPS, RTMP) из базы данных и перезагрузить nginx. | -| `migration` | Очистить базу миграций (`xc_vm_migrate`) и опционально восстановить `.sql`-бэкап. | +| `migration` | Очистить промежуточную БД (`xc_vm_migrate`) и опционально восстановить `.sql`-бэкап. | | `user` | Создать rescue-администратора со случайными учётными данными. Выводит логин и пароль. **Удалите пользователя после использования!** | | `mysql` | Переавторизовать привилегии MySQL для всех серверов балансировки нагрузки. | | `database` | Восстановить чистую БД XC_VM из `database.sql`. **Удаляет ВСЕ данные!** Требует флаг `--confirm`. | @@ -257,10 +257,10 @@ sudo /home/xc_vm/console.php tools access # Перегенерация конфигурации портов (root) sudo /home/xc_vm/console.php tools ports -# Очистить базу миграций (root) +# Очистить промежуточную БД (root) sudo /home/xc_vm/console.php tools migration -# Очистить базу миграций и восстановить бэкап (root) +# Очистить промежуточную БД и восстановить бэкап (root) sudo /home/xc_vm/console.php tools migration /path/to/backup.sql # Создать rescue-администратора (root) @@ -292,13 +292,13 @@ su - xc_vm -c '/home/xc_vm/console.php tools bouquets' --- -## Миграции базы данных +## Обновление БД после обновления версии -XC_VM использует файловую систему миграций для управления изменениями схемы БД между версиями. Миграции выполняются автоматически при обновлениях и проверке статуса системы. +XC_VM использует файловую систему обновления БД для управления изменениями схемы между версиями. Обновление БД выполняется автоматически при обновлениях и проверке статуса системы. ### Как это работает -- Файлы миграций хранятся как `.sql` файлы в `/home/xc_vm/migrations/`. +- SQL-файлы обновления БД хранятся в `/home/xc_vm/migrations/`. - Каждый файл именуется с числовым префиксом, например: @@ -308,16 +308,16 @@ XC_VM использует файловую систему миграций дл 003_drop_settings_segment_type.sql ``` -- Применённые миграции отслеживаются в таблице `migrations` БД. Каждая миграция выполняется **ровно один раз** - если миграция уже применена, она пропускается. +- Применённые шаги обновления БД отслеживаются в таблице `migrations`. Каждый шаг выполняется **ровно один раз** - если шаг уже применён, он пропускается. -- Миграции выполняются автоматически: +- Обновление БД выполняется автоматически: - `console.php update post-update` - после обновления панели - `console.php status` - при проверке статуса системы (только на MAIN-сервере) -### Порядок выполнения миграций +### Порядок выполнения обновления БД ```text -[ MigrationRunner::run() ] +[ MigrationRunner::run() — запуск обновления БД ] │ ▼ [ CREATE TABLE IF NOT EXISTS `migrations` ] @@ -334,9 +334,9 @@ XC_VM использует файловую систему миграций дл --- -## Создание новой миграции +## Создание нового шага обновления БД -Когда нужно изменить схему БД (добавить колонки, создать таблицы, вставить данные и т.д.), создайте новый SQL-файл миграции. +Когда нужно изменить схему БД (добавить колонки, создать таблицы, вставить данные и т.д.), создайте новый SQL-файл шага обновления. ### Шаг 1. Выберите имя файла @@ -365,7 +365,7 @@ NNN_short_description.sql Поместите SQL-запросы в файл. Несколько запросов разделяются `;`. -**Правила для SQL миграций:** +**Правила для SQL-шагов обновления БД:** - **Используйте `IF EXISTS` / `IF NOT EXISTS`** для идемпотентности: @@ -393,13 +393,13 @@ FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `streams_arguments` WHERE argument_key = 'my_key'); ``` -- **Не смешивайте DDL и DML**, зависящие друг от друга, в одном файле. Если нужно добавить колонку и затем заполнить её, используйте два файла миграций. +- **Не смешивайте DDL и DML**, зависящие друг от друга, в одном файле. Если нужно добавить колонку и затем заполнить её, используйте два SQL-файла обновления БД. - **Комментарии** поддерживаются с префиксом `--` (они пропускаются при выполнении). ### Шаг 3. Разместите файл -Скопируйте файл миграции в: +Скопируйте SQL-файл шага обновления БД в: ```text /home/xc_vm/migrations/ @@ -407,9 +407,9 @@ WHERE NOT EXISTS (SELECT 1 FROM `streams_arguments` WHERE argument_key = 'my_key > 💡 В репозитории исходного кода это `src/migrations/`. -### Шаг 4. Проверка миграции +### Шаг 4. Проверка обновления БД -Запустите команду status для применения ожидающих миграций: +Запустите команду status для применения ожидающих шагов обновления БД: ```bash sudo /home/xc_vm/console.php status first-run @@ -424,7 +424,7 @@ Migrations ``` -Если запрос завершился с ошибкой, миграция всё равно будет записана, но покажет `[WARN]` — проверьте SQL и исправьте проблемы. +Если запрос завершился с ошибкой, шаг всё равно будет записан, но покажет `[WARN]` — проверьте SQL и исправьте проблемы. --- @@ -436,7 +436,7 @@ Migrations sudo /home/xc_vm/console.php status ``` -Проверяет, запущен ли XC_VM, подключается к БД, выполняет ожидающие миграции, исправляет права доступа и валидирует конфигурацию nginx. Требуется после установки или восстановления. +Проверяет, запущен ли XC_VM, подключается к БД, выполняет ожидающие шаги обновления БД, исправляет права доступа и валидирует конфигурацию nginx. Требуется после установки или восстановления. С аргументом `first-run` пропускает проверку статуса — используется при первоначальной настройке: @@ -472,12 +472,12 @@ sudo -u xc_vm /home/xc_vm/console.php monitor sudo /home/xc_vm/console.php certbot ``` -### Миграция (с других систем) +### Обновление БД данными из других систем ```bash /home/xc_vm/console.php migrate ``` -Переносит данные из базы миграции. Подробности в [Руководстве по миграции](ru-ru/info/migration_guide.md). +Переносит данные из промежуточной БД `xc_vm_migrate`. Подробности в [Руководстве по обновлению БД](ru-ru/info/migration_guide.md). --- diff --git a/src/cli/Commands/CacheHandlerCommand.php b/src/cli/Commands/CacheHandlerCommand.php index 2aba8067..4a2c3eaa 100644 --- a/src/cli/Commands/CacheHandlerCommand.php +++ b/src/cli/Commands/CacheHandlerCommand.php @@ -99,7 +99,9 @@ class CacheHandlerCommand implements CommandInterface { touch(FLOOD_TMP_PATH . 'block_' . $rIP); break; } - unlink($rFileMD5); + if (file_exists($rFileMD5)) { + unlink($rFileMD5); + } } $rUpdatedLines = array_unique($rUpdatedLines); foreach ($rUpdatedLines as $rUserID) { diff --git a/src/cli/Commands/WatchdogCommand.php b/src/cli/Commands/WatchdogCommand.php index d16c70d7..b2e8a428 100644 --- a/src/cli/Commands/WatchdogCommand.php +++ b/src/cli/Commands/WatchdogCommand.php @@ -102,7 +102,7 @@ class WatchdogCommand implements CommandInterface { // ── PHP PIDs ───────────────────────────────────────── $rPHPPIDs = array(); - exec("ps -u xc_vm | grep php-fpm | awk {'print \$1'}", $rPHPPIDs); + @exec("ps -u xc_vm | grep php-fpm | awk {'print \$1'}", $rPHPPIDs); // ── Update servers table ───────────────────────────── $rConnections = $rUsers = 0; diff --git a/src/cli/CronJobs/CacheEngineCronJob.php b/src/cli/CronJobs/CacheEngineCronJob.php index 0789d35b..feb3c818 100644 --- a/src/cli/CronJobs/CacheEngineCronJob.php +++ b/src/cli/CronJobs/CacheEngineCronJob.php @@ -315,8 +315,12 @@ class CacheEngineCronJob implements CommandInterface { } else { foreach ([STREAMS_TMP_PATH, LINES_TMP_PATH, SERIES_TMP_PATH] as $rTmpPath) { foreach (scandir($rTmpPath) as $rFile) { - if (filemtime($rTmpPath . $rFile) < $rStartTime - 1) { - unlink($rTmpPath . $rFile); + if ($rFile === '.' || $rFile === '..') { + continue; + } + $rFilePath = $rTmpPath . $rFile; + if (is_file($rFilePath) && filemtime($rFilePath) < $rStartTime - 1) { + unlink($rFilePath); } } } @@ -337,7 +341,13 @@ class CacheEngineCronJob implements CommandInterface { echo 'Clearing old data...' . "\n"; foreach ([STREAMS_TMP_PATH, LINES_TMP_PATH, SERIES_TMP_PATH] as $rTmpPath) { foreach (scandir($rTmpPath) as $rFile) { - unlink($rTmpPath . $rFile); + if ($rFile === '.' || $rFile === '..') { + continue; + } + $rFilePath = $rTmpPath . $rFile; + if (is_file($rFilePath)) { + unlink($rFilePath); + } } } file_put_contents(CACHE_TMP_PATH . 'cache_complete', time()); @@ -406,7 +416,14 @@ class CacheEngineCronJob implements CommandInterface { $rCount = count($cacheLockMechanism); } if ($rCount > 0) { - $rBouquetMap = igbinary_unserialize(file_get_contents(CACHE_TMP_PATH . 'bouquet_map')); + $rBouquetMap = []; + $rBouquetMapPath = CACHE_TMP_PATH . 'bouquet_map'; + if (file_exists($rBouquetMapPath) && 0 < filesize($rBouquetMapPath)) { + $rBouquetData = @igbinary_unserialize(file_get_contents($rBouquetMapPath)); + if (is_array($rBouquetData)) { + $rBouquetMap = $rBouquetData; + } + } if (!is_null($rStart)) { $rEnd = $rStart + $rCount - 1; if ($this->rSplit >= ($rEnd - $rStart + 1)) { diff --git a/src/cli/CronJobs/RootMysqlCronJob.php b/src/cli/CronJobs/RootMysqlCronJob.php index 06a92404..45e80fa5 100644 --- a/src/cli/CronJobs/RootMysqlCronJob.php +++ b/src/cli/CronJobs/RootMysqlCronJob.php @@ -65,7 +65,17 @@ class RootMysqlCronJob implements CommandInterface { exec('sudo tail -n 1000 /var/log/syslog | grep mysqld', $rOutput, $rRetVal); foreach ($rOutput as $rError) { - $rStrip = trim(explode(']:', explode('mysqld[', $rError)[1])[1]); + $rMySQLDParts = explode('mysqld[', $rError, 2); + if (count($rMySQLDParts) < 2) { + continue; + } + + $rErrorParts = explode(']:', $rMySQLDParts[1], 2); + if (count($rErrorParts) < 2) { + continue; + } + + $rStrip = trim($rErrorParts[1]); $rTime = strtotime(substr($rStrip, 0, 19)); if ($rMaxTime >= $rTime) { diff --git a/src/cli/DaemonTrait.php b/src/cli/DaemonTrait.php index 63454f24..0d9fcc1f 100644 --- a/src/cli/DaemonTrait.php +++ b/src/cli/DaemonTrait.php @@ -107,7 +107,7 @@ trait DaemonTrait { if (is_object($db)) { $db->close_mysql(); } - shell_exec('(sleep 1; ' . PHP_BIN . ' ' . MAIN_HOME . 'console.php ' . escapeshellarg($rCommandName) . ') > /dev/null 2>/dev/null &'); + @shell_exec('(sleep 1; ' . PHP_BIN . ' ' . MAIN_HOME . 'console.php ' . escapeshellarg($rCommandName) . ') > /dev/null 2>/dev/null &'); } /** diff --git a/src/core/Util/SystemInfo.php b/src/core/Util/SystemInfo.php index 30b16bff..564b5474 100644 --- a/src/core/Util/SystemInfo.php +++ b/src/core/Util/SystemInfo.php @@ -26,9 +26,9 @@ class SystemInfo { public static function getStats() { $rJSON = array(); $rJSON['cpu'] = round(self::getTotalCPU(), 2); - $rJSON['cpu_cores'] = intval(shell_exec('cat /proc/cpuinfo | grep "^processor" | wc -l')); + $rJSON['cpu_cores'] = intval(@shell_exec('cat /proc/cpuinfo | grep "^processor" | wc -l') ?? '0'); $rJSON['cpu_avg'] = round((sys_getloadavg()[0] * 100) / (($rJSON['cpu_cores'] ?: 1)), 2); - $rJSON['cpu_name'] = trim(shell_exec("cat /proc/cpuinfo | grep 'model name' | uniq | awk -F: '{print \$2}'")); + $rJSON['cpu_name'] = trim(@shell_exec("cat /proc/cpuinfo | grep 'model name' | uniq | awk -F: '{print \$2}'") ?? ''); if ($rJSON['cpu_avg'] > 100) { $rJSON['cpu_avg'] = 100; } @@ -39,9 +39,9 @@ class SystemInfo { $rJSON['total_mem_used_percent'] = round(($rJSON['total_mem_used'] / $rJSON['total_mem']) * 100, 2); $rJSON['total_disk_space'] = disk_total_space(MAIN_HOME); $rJSON['free_disk_space'] = disk_free_space(MAIN_HOME); - $rJSON['kernel'] = trim(shell_exec('uname -r')); + $rJSON['kernel'] = trim(@shell_exec('uname -r') ?? ''); $rJSON['uptime'] = self::getUptime(); - $rJSON['total_running_streams'] = (int) trim(shell_exec('ps ax | grep -v grep | grep -c ffmpeg')); + $rJSON['total_running_streams'] = (int) trim(@shell_exec('ps ax | grep -v grep | grep -c ffmpeg') ?? '0'); $rJSON['bytes_sent'] = 0; $rJSON['bytes_sent_total'] = 0; $rJSON['bytes_received'] = 0; @@ -84,16 +84,16 @@ class SystemInfo { $rJSON['video_devices'] = $rJSON['audio_devices']; $rJSON['gpu_info'] = $rJSON['video_devices']; $rJSON['iostat_info'] = $rJSON['gpu_info']; - if (shell_exec('which iostat')) { + if (@shell_exec('which iostat')) { $rJSON['iostat_info'] = self::getIO(); } - if (shell_exec('which nvidia-smi')) { + if (@shell_exec('which nvidia-smi')) { $rJSON['gpu_info'] = self::getGPUInfo(); } - if (shell_exec('which v4l2-ctl')) { + if (@shell_exec('which v4l2-ctl')) { $rJSON['video_devices'] = self::getVideoDevices(); } - if (shell_exec('which arecord')) { + if (@shell_exec('which arecord')) { $rJSON['audio_devices'] = self::getAudioDevices(); } list($rJSON['cpu_load_average']) = sys_getloadavg(); @@ -107,7 +107,8 @@ class SystemInfo { */ public static function getTotalCPU() { $rTotalLoad = 0; - exec('ps -Ao pid,pcpu', $processes); + $processes = array(); + @exec('ps -Ao pid,pcpu', $processes); foreach ($processes as $process) { $cols = explode(' ', preg_replace('!\\s+!', ' ', trim($process))); if (count($cols) >= 2 && is_numeric($cols[1])) { @@ -116,7 +117,7 @@ class SystemInfo { } $cpuCores = 1; - $coreCount = intval(shell_exec("grep -P '^processor' /proc/cpuinfo|wc -l")); + $coreCount = intval(@shell_exec("grep -P '^processor' /proc/cpuinfo|wc -l") ?? '0'); if ($coreCount > 0) { $cpuCores = $coreCount; } @@ -183,7 +184,8 @@ class SystemInfo { */ public static function getNetworkInterfaces() { $rReturn = array(); - exec('ls /sys/class/net/', $rOutput, $rReturnVar); + $rOutput = array(); + @exec('ls /sys/class/net/', $rOutput, $rReturnVar); foreach ($rOutput as $rInterface) { $rInterface = trim(rtrim($rInterface, ':')); if (!($rInterface != 'lo' && substr($rInterface, 0, 4) != 'bond')) { @@ -222,7 +224,7 @@ class SystemInfo { $rReturn = array(); $rID = 0; try { - $rDevices = array_values(array_filter(explode("\n", shell_exec('v4l2-ctl --list-devices') ?? ''))); + $rDevices = array_values(array_filter(explode("\n", @shell_exec('v4l2-ctl --list-devices') ?? ''))); if (is_array($rDevices)) { foreach ($rDevices as $rKey => $rValue) { if ($rKey % 2 == 0) { @@ -244,7 +246,7 @@ class SystemInfo { */ public static function getAudioDevices() { try { - return array_filter(explode("\n", shell_exec('arecord -L | grep "hw:CARD="') ?? '')); + return array_filter(explode("\n", @shell_exec('arecord -L | grep "hw:CARD="') ?? '')); } catch (Exception $e) { return array(); } @@ -256,7 +258,8 @@ class SystemInfo { * @return array */ public static function getIO() { - exec('iostat -o JSON -m', $rOutput, $rReturnVar); + $rOutput = array(); + @exec('iostat -o JSON -m', $rOutput, $rReturnVar); $rOutput = implode('', $rOutput); $rJSON = json_decode($rOutput, true); if (isset($rJSON['sysstat'])) { @@ -271,7 +274,8 @@ class SystemInfo { * @return array */ public static function getGPUInfo() { - exec('nvidia-smi -x -q', $rOutput, $rReturnVar); + $rOutput = array(); + @exec('nvidia-smi -x -q', $rOutput, $rReturnVar); $rOutput = implode('', $rOutput); if (stripos($rOutput, 'query('SELECT `monitor_pid` FROM `streams_servers` WHERE `server_id` = ? AND `stream_id` = ? LIMIT 1;', SERVER_ID, $rStreamID); - $rMonitor = intval($db->get_row()['monitor_pid']); + $rStreamServer = $db->get_row(); + $rMonitor = intval($rStreamServer['monitor_pid'] ?? 0); } if (0 < $rMonitor && ProcessChecker::checkPID($rMonitor, array('XC_VM[' . $rStreamID . ']', 'XC_VMProxy[' . $rStreamID . ']')) && is_numeric($rMonitor)) { @@ -222,7 +223,8 @@ class StreamProcess { $rPID = intval(file_get_contents(STREAMS_PATH . $rStreamID . '_.pid')); } else { $db->query('SELECT `pid` FROM `streams_servers` WHERE `server_id` = ? AND `stream_id` = ? LIMIT 1;', SERVER_ID, $rStreamID); - $rPID = intval($db->get_row()['pid']); + $rStreamServer = $db->get_row(); + $rPID = intval($rStreamServer['pid'] ?? 0); } if (0 < $rPID && ProcessChecker::checkPID($rPID, array($rStreamID . '_.m3u8', $rStreamID . '_%d.ts', 'LLOD[' . $rStreamID . ']', 'XC_VMProxy[' . $rStreamID . ']', 'Loopback[' . $rStreamID . ']')) && is_numeric($rPID)) { diff --git a/src/domain/User/UserRepository.php b/src/domain/User/UserRepository.php index 8e170d54..198dcb14 100644 --- a/src/domain/User/UserRepository.php +++ b/src/domain/User/UserRepository.php @@ -134,16 +134,20 @@ class UserRepository { if ($rCached) { if (empty($rPassword) && empty($rUserID) && strlen($rUsername) == 32) { if ($rSettings['case_sensitive_line']) { - $rUserID = intval(file_get_contents(LINES_TMP_PATH . 'line_t_' . $rUsername)); + $rTokenPath = LINES_TMP_PATH . 'line_t_' . $rUsername; + $rUserID = (file_exists($rTokenPath) ? intval(file_get_contents($rTokenPath)) : 0); } else { - $rUserID = intval(file_get_contents(LINES_TMP_PATH . 'line_t_' . strtolower($rUsername))); + $rTokenPath = LINES_TMP_PATH . 'line_t_' . strtolower($rUsername); + $rUserID = (file_exists($rTokenPath) ? intval(file_get_contents($rTokenPath)) : 0); } } else { if (!empty($rUsername) && !empty($rPassword)) { if ($rSettings['case_sensitive_line']) { - $rUserID = intval(file_get_contents(LINES_TMP_PATH . 'line_c_' . $rUsername . '_' . $rPassword)); + $rCachePath = LINES_TMP_PATH . 'line_c_' . $rUsername . '_' . $rPassword; + $rUserID = (file_exists($rCachePath) ? intval(file_get_contents($rCachePath)) : 0); } else { - $rUserID = intval(file_get_contents(LINES_TMP_PATH . 'line_c_' . strtolower($rUsername) . '_' . strtolower($rPassword))); + $rCachePath = LINES_TMP_PATH . 'line_c_' . strtolower($rUsername) . '_' . strtolower($rPassword); + $rUserID = (file_exists($rCachePath) ? intval(file_get_contents($rCachePath)) : 0); } } else { if (empty($rUserID)) { @@ -153,7 +157,12 @@ class UserRepository { } if ($rUserID) { - $rUserInfo = igbinary_unserialize(file_get_contents(LINES_TMP_PATH . 'line_i_' . $rUserID)); + $rInfoPath = LINES_TMP_PATH . 'line_i_' . $rUserID; + if (file_exists($rInfoPath)) { + $rUserInfo = igbinary_unserialize(file_get_contents($rInfoPath)); + } else { + return false; + } } } else { if (empty($rPassword) && empty($rUserID) && strlen($rUsername) == 32) { @@ -309,16 +318,20 @@ class UserRepository { if ($rCached) { if (empty($rPassword) && empty($rUserID) && strlen($rUsername) == 32) { if ($rSettings['case_sensitive_line']) { - $rUserID = intval(file_get_contents(LINES_TMP_PATH . 'line_t_' . $rUsername)); + $rTokenPath = LINES_TMP_PATH . 'line_t_' . $rUsername; + $rUserID = (file_exists($rTokenPath) ? intval(file_get_contents($rTokenPath)) : 0); } else { - $rUserID = intval(file_get_contents(LINES_TMP_PATH . 'line_t_' . strtolower($rUsername))); + $rTokenPath = LINES_TMP_PATH . 'line_t_' . strtolower($rUsername); + $rUserID = (file_exists($rTokenPath) ? intval(file_get_contents($rTokenPath)) : 0); } } else { if (!empty($rUsername) && !empty($rPassword)) { if ($rSettings['case_sensitive_line']) { - $rUserID = intval(file_get_contents(LINES_TMP_PATH . 'line_c_' . $rUsername . '_' . $rPassword)); + $rCachePath = LINES_TMP_PATH . 'line_c_' . $rUsername . '_' . $rPassword; + $rUserID = (file_exists($rCachePath) ? intval(file_get_contents($rCachePath)) : 0); } else { - $rUserID = intval(file_get_contents(LINES_TMP_PATH . 'line_c_' . strtolower($rUsername) . '_' . strtolower($rPassword))); + $rCachePath = LINES_TMP_PATH . 'line_c_' . strtolower($rUsername) . '_' . strtolower($rPassword); + $rUserID = (file_exists($rCachePath) ? intval(file_get_contents($rCachePath)) : 0); } } else { if (!empty($rUserID)) { @@ -329,7 +342,12 @@ class UserRepository { } if (!$rUserID) { } else { - $rUserInfo = igbinary_unserialize(file_get_contents(LINES_TMP_PATH . 'line_i_' . $rUserID)); + $rInfoPath = LINES_TMP_PATH . 'line_i_' . $rUserID; + if (file_exists($rInfoPath)) { + $rUserInfo = igbinary_unserialize(file_get_contents($rInfoPath)); + } else { + return false; + } } } else { if (empty($rPassword) && empty($rUserID) && strlen($rUsername) == 32) { diff --git a/src/domain/Vod/EpisodeService.php b/src/domain/Vod/EpisodeService.php index 2a629b27..7d3e480e 100644 --- a/src/domain/Vod/EpisodeService.php +++ b/src/domain/Vod/EpisodeService.php @@ -31,11 +31,24 @@ class EpisodeService { } } - $rArray['stream_source'] = array($rData['stream_source']); + $rFallbackStreamSource = ''; + if (isset($rArray['stream_source'])) { + if (is_array($rArray['stream_source'])) { + $rFallbackStreamSource = $rArray['stream_source'][0] ?? ''; + } else { + $rFallbackStreamSource = $rArray['stream_source']; + } + } + $rArray['stream_source'] = array($rData['stream_source'] ?? $rFallbackStreamSource); - if (0 < strlen($rData['movie_subtitles'])) { - $rSplit = explode(':', $rData['movie_subtitles']); - $rArray['movie_subtitles'] = array('files' => array($rSplit[2]), 'names' => array('Subtitles'), 'charset' => array('UTF-8'), 'location' => intval($rSplit[1])); + $rMovieSubtitles = $rData['movie_subtitles'] ?? ''; + if (0 < strlen($rMovieSubtitles)) { + $rSplit = explode(':', $rMovieSubtitles); + if (2 < count($rSplit) && 0 < strlen($rSplit[2])) { + $rArray['movie_subtitles'] = array('files' => array($rSplit[2]), 'names' => array('Subtitles'), 'charset' => array('UTF-8'), 'location' => intval($rSplit[1] ?? 0)); + } else { + $rArray['movie_subtitles'] = null; + } } else { $rArray['movie_subtitles'] = null; } @@ -108,7 +121,7 @@ class EpisodeService { $rSeconds = intval($rSeries['episode_run_time']) * 60; $rImportArray['properties'] = array('tmdb_id' => $rEpisode['id'], 'release_date' => $rEpisode['air_date'], 'plot' => $rEpisode['overview'], 'duration_secs' => $rSeconds, 'duration' => sprintf('%02d:%02d:%02d', $rSeconds / 3600, ($rSeconds / 60) % 60, $rSeconds % 60), 'movie_image' => $rImage, 'video' => array(), 'audio' => array(), 'bitrate' => 0, 'rating' => $rEpisode['vote_average'], 'season' => $rData['season_num']); - if (strlen($rImportArray['properties']['movie_image'][0]) == 0) { + if (empty($rImportArray['properties']['movie_image'])) { unset($rImportArray['properties']['movie_image']); } } @@ -130,14 +143,15 @@ class EpisodeService { } else { $rImportArray = array('filename' => $rArray['stream_source'][0], 'properties' => array(), 'name' => $rArray['stream_display_name'], 'episode' => $rData['episode'], 'target_container' => $rData['target_container']); - if ($rSettings['download_images']) { - $rData['movie_image'] = ImageUtils::downloadImage($rData['movie_image'], 5); + $rMovieImage = $rData['movie_image'] ?? ''; + if ($rSettings['download_images'] && 0 < strlen($rMovieImage)) { + $rMovieImage = ImageUtils::downloadImage($rMovieImage, 5); } $rSeconds = intval($rData['episode_run_time']) * 60; - $rImportArray['properties'] = array('release_date' => $rData['release_date'], 'plot' => $rData['plot'], 'duration_secs' => $rSeconds, 'duration' => sprintf('%02d:%02d:%02d', $rSeconds / 3600, ($rSeconds / 60) % 60, $rSeconds % 60), 'movie_image' => $rData['movie_image'], 'video' => array(), 'audio' => array(), 'bitrate' => 0, 'rating' => $rData['rating'], 'season' => $rData['season_num'], 'tmdb_id' => $rData['tmdb_id']); + $rImportArray['properties'] = array('release_date' => $rData['release_date'], 'plot' => $rData['plot'], 'duration_secs' => $rSeconds, 'duration' => sprintf('%02d:%02d:%02d', $rSeconds / 3600, ($rSeconds / 60) % 60, $rSeconds % 60), 'movie_image' => $rMovieImage, 'video' => array(), 'audio' => array(), 'bitrate' => 0, 'rating' => $rData['rating'], 'season' => $rData['season_num'], 'tmdb_id' => $rData['tmdb_id']); - if (strlen($rImportArray['properties']['movie_image'][0]) == 0) { + if (empty($rImportArray['properties']['movie_image'])) { unset($rImportArray['properties']['movie_image']); } @@ -211,7 +225,10 @@ class EpisodeService { $rRestartIDs[] = $rInsertID; } - $db->query('UPDATE `streams_series` SET `last_modified` = ? WHERE `id` = ?;', time(), $rData['streams_series']); + $rSeriesID = intval($rData['streams_series'] ?? $rData['series'] ?? 0); + if (0 < $rSeriesID) { + $db->query('UPDATE `streams_series` SET `last_modified` = ? WHERE `id` = ?;', time(), $rSeriesID); + } StreamProcess::updateStream($rInsertID); } else { return array('status' => STATUS_FAILURE); diff --git a/src/public/Controllers/Admin/EpisodeController.php b/src/public/Controllers/Admin/EpisodeController.php index 177f6c32..6c6d889e 100644 --- a/src/public/Controllers/Admin/EpisodeController.php +++ b/src/public/Controllers/Admin/EpisodeController.php @@ -31,6 +31,9 @@ class EpisodeController extends BaseAdminController { return; } + $rEpisode = null; + $rStreamSys = []; + if (isset(RequestManager::getAll()['id'])) { $rEpisode = StreamRepository::getById(RequestManager::getAll()['id']); if (!$rEpisode || $rEpisode['type'] != 5) { diff --git a/src/public/Controllers/Admin/StreamCategoryController.php b/src/public/Controllers/Admin/StreamCategoryController.php index 6a7cad74..8c606798 100644 --- a/src/public/Controllers/Admin/StreamCategoryController.php +++ b/src/public/Controllers/Admin/StreamCategoryController.php @@ -15,6 +15,7 @@ class StreamCategoryController extends BaseAdminController { public function index() { $this->requirePermission(); + $rCategoryArr = null; if (isset(RequestManager::getAll()['id'])) { $rCategoryArr = CategoryService::getById(RequestManager::getAll()['id']); diff --git a/src/public/Controllers/Api/PlayerApiController.php b/src/public/Controllers/Api/PlayerApiController.php index 1d2b10db..199b55e8 100644 --- a/src/public/Controllers/Api/PlayerApiController.php +++ b/src/public/Controllers/Api/PlayerApiController.php @@ -69,6 +69,7 @@ class PlayerApiController { if ($this->panelAPI && empty($rAction) || in_array($rAction, array('get_vod_categories', 'get_series_categories', 'get_live_categories'))) { $rCategories = CacheReader::get('categories'); } + $rUserInfo = null; if (isset($rRequest['username']) && isset($rRequest['password'])) { $rUsername = $rRequest['username']; diff --git a/src/public/Views/admin/post.php b/src/public/Views/admin/post.php index dd7ff091..1118838a 100644 --- a/src/public/Views/admin/post.php +++ b/src/public/Views/admin/post.php @@ -1354,30 +1354,32 @@ if (1 < $rICount) { ?> case 'episode': $rReturn = EpisodeService::process($rData); + $rStatus = (is_array($rReturn) ? ($rReturn['status'] ?? STATUS_FAILURE) : STATUS_FAILURE); + $rReturnData = (is_array($rReturn) && isset($rReturn['data']) && is_array($rReturn['data']) ? $rReturn['data'] : array()); - if ($rReturn['status'] == STATUS_SUCCESS) { + if ($rStatus == STATUS_SUCCESS) { if (isset($rData['edit']) && AdminHelpers::getPageFromURL($rReferer) == 'episodes') { - echo json_encode(array('result' => true, 'location' => $rReferer, 'status' => $rReturn['status'])); + echo json_encode(array('result' => true, 'location' => $rReferer, 'status' => $rStatus)); exit(); } - echo json_encode(array('result' => true, 'location' => 'stream_view?sid=' . intval($rReturn['data']['series_id']) . '&id=' . intval($rReturn['data']['insert_id']) . '&status=' . intval($rReturn['status']))); + echo json_encode(array('result' => true, 'location' => 'stream_view?sid=' . intval($rReturnData['series_id'] ?? 0) . '&id=' . intval($rReturnData['insert_id'] ?? 0) . '&status=' . intval($rStatus))); exit(); } - if ($rReturn['status'] == STATUS_SUCCESS_MULTI) { + if ($rStatus == STATUS_SUCCESS_MULTI) { if (isset($rData['edit']) && AdminHelpers::getPageFromURL($rReferer) == 'episodes') { - echo json_encode(array('result' => true, 'location' => $rReferer, 'status' => $rReturn['status'])); + echo json_encode(array('result' => true, 'location' => $rReferer, 'status' => $rStatus)); exit(); } - echo json_encode(array('result' => true, 'location' => 'episodes?series=' . intval($rReturn['data']['series_id']) . '&status=' . intval($rReturn['status']), 'status' => $rReturn['status'])); + echo json_encode(array('result' => true, 'location' => 'episodes?series=' . intval($rReturnData['series_id'] ?? 0) . '&status=' . intval($rStatus), 'status' => $rStatus)); exit(); } - echo json_encode(array('result' => false, 'data' => $rReturn['data'], 'status' => $rReturn['status'])); + echo json_encode(array('result' => false, 'data' => $rReturnData, 'status' => $rStatus)); exit(); case 'episodes_mass': diff --git a/src/public/Views/admin/server.php b/src/public/Views/admin/server.php index 933fbb7e..719abf6b 100644 --- a/src/public/Views/admin/server.php +++ b/src/public/Views/admin/server.php @@ -454,7 +454,13 @@ 0): $rCurrentGovernor = json_decode($rServerArr['governor'], true); - $rCurrentGovernor[3] = '* ' . $rCurrentGovernor[2] . ' - Freq: ' . round($rCurrentGovernor[0] / 1000000, 1) . 'GHz - ' . round($rCurrentGovernor[1] / 1000000, 1) . 'GHz'; ?> + if (!is_array($rCurrentGovernor)) { + $rCurrentGovernor = array(); + } + $rGovernorMin = floatval($rCurrentGovernor[0] ?? 0); + $rGovernorMax = floatval($rCurrentGovernor[1] ?? 0); + $rCurrentGovernor[2] = ($rCurrentGovernor[2] ?? ''); + $rCurrentGovernor[3] = '* ' . $rCurrentGovernor[2] . ' - Freq: ' . round($rGovernorMin / 1000000, 1) . 'GHz - ' . round($rGovernorMax / 1000000, 1) . 'GHz'; ?>