*/ protected $guarded = []; /** * @var bool */ public $timestamps = false; /** * @return HasMany */ public function alias(): HasMany { return $this->hasMany(VideoAlias::class, 'videos_id'); } /** * @return HasMany */ public function release(): HasMany { return $this->hasMany(Release::class, 'videos_id'); } /** * @return HasMany */ public function episode(): HasMany { return $this->hasMany(TvEpisode::class, 'videos_id'); } /** * @return HasOne */ public function tvInfo(): HasOne { return $this->hasOne(TvInfo::class, 'videos_id'); } /** * Get info from tables for the provided ID. * * * @return Model|null|static */ public static function getByVideoID(mixed $id) { return self::query() ->select(['videos.*', 'tv_info.summary', 'tv_info.publisher', 'tv_info.image']) ->where('videos.id', $id) ->join('tv_info', 'videos.id', '=', 'tv_info.videos_id') ->first(); } /** * Retrieves a range of all shows for the show-edit admin list. */ public static function getRange(string $showname = ''): LengthAwarePaginator // @phpstan-ignore missingType.generics { $sql = self::query() ->select(['videos.*', 'tv_info.summary', 'tv_info.publisher', 'tv_info.image']) ->join('tv_info', 'videos.id', '=', 'tv_info.videos_id'); if ($showname !== '') { $sql->where('videos.title', 'like', '%'.$showname.'%'); } return $sql->paginate(config('nntmux.items_per_page')); } /** * Returns a count of all shows -- usually used by pager. */ public static function getCount(string $showname = ''): int { $res = self::query()->join('tv_info', 'videos.id', '=', 'tv_info.videos_id'); if ($showname !== '') { $res->where('videos.title', 'like', '%'.$showname.'%'); } return $res->count('videos.id'); } /** * Retrieves and returns a list of shows with eligible releases. * * @return array */ public static function getSeriesList(mixed $uid, string $letter = '', string $showname = ''): array { $cacheKey = 'video_series_list:'.md5(serialize([(string) $uid, $letter, $showname])); return Cache::remember($cacheKey, now()->addMinutes((int) config('nntmux.cache_expiry_medium', 60)), function () use ($uid, $letter, $showname): array { return self::getSeriesListUncached($uid, $letter, $showname); }); } /** * @internal Used by {@see getSeriesList} with cache wrapper. */ private static function getSeriesListUncached(mixed $uid, string $letter = '', string $showname = ''): array { $params = [ 'uid' => $uid, 'tv_root' => Category::TV_ROOT, 'tv_other' => Category::TV_OTHER, 'now' => now()->toDateTimeString(), ]; $letterCondition = ''; if ($letter !== '') { if ($letter === '0-9' || $letter === '[0-9]') { $letterCondition = "AND videos.title REGEXP '^[0-9]'"; } else { $letterCondition = 'AND videos.title LIKE :letter'; $params['letter'] = $letter.'%'; } } $shownameCondition = ''; if ($showname !== '') { if (Search::isAvailable()) { $hits = Search::searchTvShows($showname, 800); $vids = array_values(array_filter(array_map(static fn ($id): int => (int) $id, $hits['id'] ?? []))); if ($vids === []) { return []; } $shownameCondition = 'AND videos.id IN ('.implode(',', $vids).')'; } else { $shownameCondition = 'AND videos.title LIKE :showname'; $params['showname'] = '%'.$showname.'%'; } } $sql = " SELECT videos.*, tve.firstaired AS prevdate, tve.title AS previnfo, tvi.publisher, us.id AS userseriesid FROM videos INNER JOIN tv_info AS tvi ON videos.id = tvi.videos_id INNER JOIN ( SELECT videos_id, MAX(firstaired) AS max_firstaired FROM tv_episodes WHERE firstaired < :now GROUP BY videos_id ) AS latest_ep ON videos.id = latest_ep.videos_id INNER JOIN tv_episodes AS tve ON videos.id = tve.videos_id AND tve.firstaired = latest_ep.max_firstaired LEFT JOIN user_series AS us ON videos.id = us.videos_id AND us.users_id = :uid WHERE EXISTS ( SELECT 1 FROM releases AS r WHERE r.videos_id = videos.id AND r.categories_id BETWEEN :tv_root AND :tv_other ) {$letterCondition} {$shownameCondition} GROUP BY videos.id ORDER BY videos.title ASC "; $results = DB::select($sql, $params); // Convert stdClass objects to arrays for backward compatibility return array_map(fn ($row) => (array) $row, $results); } }