diff --git a/src/Domain/Stream/CategoryService.php b/src/Domain/Stream/CategoryService.php index 76ef1521..230eeecb 100644 --- a/src/Domain/Stream/CategoryService.php +++ b/src/Domain/Stream/CategoryService.php @@ -45,7 +45,9 @@ class CategoryService { } } FileCache::delCache('categories'); - FileCache::delCache('category_map'); + // category_map (bouquet => category ids) is left alone: reordering does not + // change it, and only the heavy cache pass rebuilds it — up to 5 minutes + // later — while every line authentication reads it. } if (!defined('STATUS_SUCCESS') && class_exists(\XC_Bootstrap::class)) { @@ -88,7 +90,8 @@ class CategoryService { $rInsertID = $db->last_insert_id(); $catId = isset($rData['edit']) ? intval($rData['edit']) : intval($rInsertID); FileCache::delCache('categories'); - FileCache::delCache('category_map'); + // Not category_map: which categories a bouquet reaches depends on its + // streams, not on a category's name, type or order (see reorder()). // Sync any templates and subscribers tied to this category if ($catId > 0 && class_exists(CategoryTemplateService::class)) { @@ -105,10 +108,11 @@ class CategoryService { * Возвращает категории с int-ключами (замена legacy getCategories()). * Всегда читает из БД ($rForce = true). * - * @param string $rType 'live'|'movie'|'series'|'radio'|null + * @param string|null $rType 'live'|'movie'|'series'|'radio', or null for every type + * (the admin global search asks for all of them). * @return array */ - public static function getAllByType(string $rType = 'live') { + public static function getAllByType(?string $rType = 'live') { $rCategories = self::getFromDatabase(($rType ?: null), true); $rReturn = []; foreach ($rCategories as $rID => $rRow) { diff --git a/src/Domain/Stream/StreamProcess.php b/src/Domain/Stream/StreamProcess.php index 106aaa9b..e9e72150 100644 --- a/src/Domain/Stream/StreamProcess.php +++ b/src/Domain/Stream/StreamProcess.php @@ -315,12 +315,13 @@ class StreamProcess { * still emit `-map` for every file — so multi-subtitle movies imported one * track but mapped non-existent inputs. The two loops are now siblings. * - * @param string $rSubtitlesJson `movie_subtitles` JSON from the stream row. - * @param array $rServers Server registry (for remote subtitle fetch). + * @param string|null $rSubtitlesJson `movie_subtitles` JSON from the stream row; + * NULL for a movie or episode saved without subtitles. + * @param array $rServers Server registry (for remote subtitle fetch). * @return array{0:string,1:string} [$rSubtitlesImport, $rSubtitlesMetadata]. */ - private static function buildSubtitleImport(string $rSubtitlesJson, array $rServers) { - $rSubtitles = json_decode($rSubtitlesJson, true); + private static function buildSubtitleImport(?string $rSubtitlesJson, array $rServers) { + $rSubtitles = json_decode((string) $rSubtitlesJson, true); $rSubtitlesImport = ''; $rSubtitlesMetadata = ''; if (!empty($rSubtitles) && !empty($rSubtitles['files']) && is_array($rSubtitles['files'])) { diff --git a/src/Domain/User/UserRepository.php b/src/Domain/User/UserRepository.php index 54e7dc93..fedccd51 100644 --- a/src/Domain/User/UserRepository.php +++ b/src/Domain/User/UserRepository.php @@ -552,8 +552,10 @@ class UserRepository { $rUserInfo = array_merge($rUserInfo, self::aggregateBouquetIds($rUserInfo['bouquet'], $rBouquets)); } - $rCategoryMap = igbinary_unserialize(file_get_contents(CACHE_TMP_PATH . 'category_map')); - $rUserInfo['category_ids'] = self::resolveCategoryIds($rUserInfo['bouquet'], $rCategoryMap); + // Built by the heavy cache pass; until it exists (a fresh install, a cleared + // cache) the line simply has no categories instead of the request failing. + $rCategoryMap = @igbinary_unserialize((string) @file_get_contents(CACHE_TMP_PATH . 'category_map')); + $rUserInfo['category_ids'] = self::resolveCategoryIds($rUserInfo['bouquet'], is_array($rCategoryMap) ? $rCategoryMap : []); return $rUserInfo; } diff --git a/tests/Unit/StreamProcessSubtitleImportTest.php b/tests/Unit/StreamProcessSubtitleImportTest.php new file mode 100644 index 00000000..521878ad --- /dev/null +++ b/tests/Unit/StreamProcessSubtitleImportTest.php @@ -0,0 +1,25 @@ +setAccessible(true); + return $rMethod->invoke(null, $rJson, []); + } + + public function testNoSubtitlesImportsNothing(): void { + $this->assertSame(['', ''], $this->build(null)); + $this->assertSame(['', ''], $this->build('')); + $this->assertSame(['', ''], $this->build('{"files":[]}')); + } +}