diff --git a/src/Core/Auth/AuthRepository.php b/src/Core/Auth/AuthRepository.php index ea18ec272..15ae8c3bb 100644 --- a/src/Core/Auth/AuthRepository.php +++ b/src/Core/Auth/AuthRepository.php @@ -82,7 +82,7 @@ class AuthRepository { if ($rCode['enabled']) { $rWhitelist = array(); - foreach (json_decode($rCode['whitelist'], true) as $rIP) { + foreach ((array) json_decode($rCode['whitelist'], true) as $rIP) { if (filter_var($rIP, FILTER_VALIDATE_IP)) { $rWhitelist[] = 'allow ' . $rIP . ';'; } diff --git a/src/Core/Process/ProcessManager.php b/src/Core/Process/ProcessManager.php index aef3415b9..8c62137b3 100644 --- a/src/Core/Process/ProcessManager.php +++ b/src/Core/Process/ProcessManager.php @@ -307,18 +307,27 @@ class ProcessManager { */ public static function acquireCronLock($lockFile, $timeout = 1800) { if (file_exists($lockFile)) { - $pid = (int)trim(file_get_contents($lockFile)); + // Read content + mtime up front. A competing cron can remove the lock + // file between the exists() check and these reads (TOCTOU), which would + // otherwise emit "failed to open stream" / "stat failed" warnings. If it + // vanished, fall through and take the lock ourselves. + $contents = @file_get_contents($lockFile); + $mtime = @filemtime($lockFile); - if (self::procExists($pid)) { - // Process is running — check if it's stale - if (time() - filemtime($lockFile) >= $timeout) { - // Stale — kill and take over - if ($pid > 0) { - posix_kill($pid, 9); + if ($contents !== false && $mtime !== false) { + $pid = (int)trim($contents); + + if (self::procExists($pid)) { + // Process is running — check if it's stale + if (time() - $mtime >= $timeout) { + // Stale — kill and take over + if ($pid > 0) { + posix_kill($pid, 9); + } + } else { + // Still fresh — another instance is running + exit('Running...'); } - } else { - // Still fresh — another instance is running - exit('Running...'); } } } diff --git a/src/Public/Controllers/Player/HomeController.php b/src/Public/Controllers/Player/HomeController.php index 50f776a16..9bba6015b 100644 --- a/src/Public/Controllers/Player/HomeController.php +++ b/src/Public/Controllers/Player/HomeController.php @@ -30,9 +30,25 @@ class HomeController extends BasePlayerController } $rPopularNow = array(); - $rPopular = igbinary_unserialize(file_get_contents(CONTENT_PATH . 'tmdb_popular')); + // tmdb_popular is written by a cron and may be absent (fresh install / + // TMDB disabled) or corrupt; guard the read + unserialize so the home + // page never fatals on a missing file or a non-array payload. + $rPopular = array('movies' => array(), 'series' => array()); + $rPopularFile = CONTENT_PATH . 'tmdb_popular'; + if (is_file($rPopularFile)) { + $rPopularData = igbinary_unserialize((string) @file_get_contents($rPopularFile)); + if (is_array($rPopularData)) { + $rPopular = array_merge($rPopular, $rPopularData); + } + } + if (!is_array($rPopular['movies'])) { + $rPopular['movies'] = array(); + } + if (!is_array($rPopular['series'])) { + $rPopular['series'] = array(); + } - if (!(0 < count($rPopular['movies']) && 0 < count($rUserInfo['vod_ids']))) { + if (!(0 < count($rPopular['movies']) && 0 < count($rUserInfo['vod_ids'] ?? array()))) { } else { if (SettingsManager::getAll()['player_hide_incompatible']) { $db->query('SELECT `id`, `stream_display_name`, `year`, `rating`, `movie_properties` FROM `streams` WHERE `id` IN (' . implode(',', $rPopular['movies']) . ') AND `id` IN (' . implode(',', $rUserInfo['vod_ids']) . ') AND (SELECT MAX(`compatible`) FROM `streams_servers` WHERE `streams_servers`.`stream_id` = `streams`.`id` LIMIT 1) = 1 ORDER BY FIELD(id, ' . implode(',', $rPopular['movies']) . ') ASC LIMIT 50;'); @@ -48,7 +64,7 @@ class HomeController extends BasePlayerController } } - if (!(0 < count($rPopular['series']) && 0 < count($rUserInfo['series_ids']))) { + if (!(0 < count($rPopular['series']) && 0 < count($rUserInfo['series_ids'] ?? array()))) { } else { if (SettingsManager::getAll()['player_hide_incompatible']) { $db->query('SELECT `id`, `title`, `year`, `rating`, `cover`, `backdrop_path` FROM `streams_series` WHERE `id` IN (' . implode(',', $rPopular['series']) . ') AND `id` IN (' . implode(',', $rUserInfo['series_ids']) . ') AND (SELECT MAX(`compatible`) FROM `streams_servers` LEFT JOIN `streams_episodes` ON `streams_episodes`.`stream_id` = `streams_servers`.`stream_id` WHERE `streams_episodes`.`series_id` = `streams_series`.`id`) = 1 ORDER BY FIELD(id, ' . implode(',', $rPopular['series']) . ') ASC LIMIT 50;'); diff --git a/src/Public/Views/admin/review.php b/src/Public/Views/admin/review.php index 1343252b9..34b781e14 100644 --- a/src/Public/Views/admin/review.php +++ b/src/Public/Views/admin/review.php @@ -125,7 +125,7 @@ if (isset(RequestManager::getAll()['post_data'])) { $rExtensions = array('mp4', 'mkv', 'mov', 'avi', 'mpg', 'mpeg', 'flv', 'wmv', 'm4v'); } - if (!in_array(strtolower(pathinfo(explode('?', $rURL)[0])['extension']), $rExtensions)) { + if (!in_array(strtolower(pathinfo(explode('?', $rURL)[0])['extension'] ?? ''), $rExtensions)) { } else { $rExists = in_array(str_replace('https://', 'http://', $rURL), $rSources); diff --git a/src/Streaming/Lifecycle/ShutdownHandler.php b/src/Streaming/Lifecycle/ShutdownHandler.php index e3138ca58..5243f0579 100644 --- a/src/Streaming/Lifecycle/ShutdownHandler.php +++ b/src/Streaming/Lifecycle/ShutdownHandler.php @@ -67,7 +67,7 @@ class ShutdownHandler { } // Только live: on-demand instant off - if ($rContext === 'live' && $rSettings['on_demand_instant_off'] && $rChannelInfo['on_demand'] == 1) { + if ($rContext === 'live' && !empty($rSettings['on_demand_instant_off']) && !empty($rChannelInfo['on_demand'])) { ConnectionTracker::removeFromQueue($rStreamID, $rPID); }