ensureCliAccess(); $this->tmdbApiKey = getenv('TMDB_API_KEY') ?: $_ENV['TMDB_API_KEY']; $this->seasonsImportToken = getenv('SEASONS_IMPORT_TOKEN') ?: $_ENV['SEASONS_IMPORT_TOKEN']; $this->authenticateRequest(); } private function authenticateRequest(): void { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { $this->sendErrorResponse('Method Not Allowed', 405); } $authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? ''; if (! preg_match('/Bearer\s+(.+)/', $authHeader, $matches)) { $this->sendErrorResponse('Unauthorized', 401); } $providedToken = trim($matches[1]); if ($providedToken !== $this->seasonsImportToken) { $this->sendErrorResponse('Forbidden', 403); } } public function importSeasons(): void { $ongoingShows = $this->fetchFromApi('optimized_shows', 'ongoing=eq.true'); if (empty($ongoingShows)) { $this->sendResponse(['message' => 'No ongoing shows to update'], 200); } foreach ($ongoingShows as $show) { $this->processShowSeasons($show); } $this->sendResponse(['message' => 'Season import completed'], 200); } private function processShowSeasons(array $show): void { $tmdbId = $show['tmdb_id'] ?? null; $showId = $show['id'] ?? null; if (! $tmdbId || ! $showId) { return; } $tmdbShowData = $this->fetchShowDetails($tmdbId); $seasons = $tmdbShowData['seasons'] ?? []; $status = $tmdbShowData['status'] ?? 'Unknown'; if (empty($seasons) && ! $this->shouldKeepOngoing($status)) { $this->disableOngoingStatus($showId); return; } foreach ($seasons as $season) { $this->processSeasonEpisodes($showId, $tmdbId, $season); } } private function shouldKeepOngoing(string $status): bool { return in_array($status, ['Returning Series', 'In Production']); } private function fetchShowDetails(string $tmdbId): array { $client = new Client(); $url = "https://api.themoviedb.org/3/tv/{$tmdbId}?api_key={$this->tmdbApiKey}&append_to_response=seasons"; try { $response = $client->get($url, ['headers' => ['Accept' => 'application/json']]); return json_decode($response->getBody(), true) ?? []; } catch (\Exception $e) { return []; } } private function fetchWatchedEpisodes(int $showId): array { $episodes = $this->fetchFromApi('optimized_last_watched_episodes', "show_id=eq.{$showId}&order=last_watched_at.desc&limit=1"); if (empty($episodes)) { return []; } return [ 'season_number' => (int) $episodes[0]['season_number'], 'episode_number' => (int) $episodes[0]['episode_number'], ]; } private function processSeasonEpisodes(int $showId, string $tmdbId, array $season): void { $seasonNumber = $season['season_number'] ?? null; if ($seasonNumber === null || $seasonNumber == 0) { return; } $episodes = $this->fetchSeasonEpisodes($tmdbId, $seasonNumber); if (empty($episodes)) { return; } $watched = $this->fetchWatchedEpisodes($showId); $lastWatchedSeason = $watched['season_number'] ?? null; $lastWatchedEpisode = $watched['episode_number'] ?? null; $scheduled = $this->fetchFromApi( 'optimized_scheduled_episodes', "show_id=eq.{$showId}&season_number=eq.{$seasonNumber}" ); $scheduledEpisodeNumbers = array_column($scheduled, 'episode_number'); foreach ($episodes as $episode) { $episodeNumber = $episode['episode_number'] ?? null; if ($episodeNumber === null) { continue; } if (in_array($episodeNumber, $scheduledEpisodeNumbers)) { continue; } if ($lastWatchedSeason !== null && $seasonNumber < $lastWatchedSeason) { return; } if ($seasonNumber == $lastWatchedSeason && $episodeNumber <= $lastWatchedEpisode) { continue; } $this->addEpisodeToSchedule($showId, $seasonNumber, $episode); } } private function fetchSeasonEpisodes(string $tmdbId, int $seasonNumber): array { $client = new Client(); $url = "https://api.themoviedb.org/3/tv/{$tmdbId}/season/{$seasonNumber}?api_key={$this->tmdbApiKey}"; try { $response = $client->get($url, ['headers' => ['Accept' => 'application/json']]); return json_decode($response->getBody(), true)['episodes'] ?? []; } catch (\Exception $e) { return []; } } private function addEpisodeToSchedule(int $showId, int $seasonNumber, array $episode): void { $airDate = $episode['air_date'] ?? null; if (! $airDate) { return; } $today = date('Y-m-d'); $status = ($airDate < $today) ? 'aired' : 'upcoming'; $payload = [ 'show_id' => $showId, 'season_number' => $seasonNumber, 'episode_number' => $episode['episode_number'], 'air_date' => $airDate, 'status' => $status, ]; $this->makeRequest('POST', 'scheduled_episodes', ['json' => $payload]); } } $handler = new SeasonImportHandler(); $handler->importSeasons();