client = new Client( [ /** @var ApiToken|BearerToken */ 'api_token' => new BearerToken($apiKey), 'secure' => true, 'base_uri' => Client::TMDB_URI, 'event_dispatcher' => [ 'adapter' => $this->eventDispatcher, ], // We make use of PSR-17 and PSR-18 auto discovery to automatically guess these, but preferably set these explicitly. 'http' => [ 'client' => null, 'request_factory' => null, 'response_factory' => null, 'stream_factory' => null, 'uri_factory' => null, ], 'hydration' => [ 'event_listener_handles_hydration' => false, 'only_for_specified_models' => [] ] ] ); /** * Required event listeners and events to be registered with the PSR-14 Event Dispatcher. */ $requestListener = new RequestListener($this->client->getHttpClient(), $this->eventDispatcher); $this->eventDispatcher->addListener(RequestEvent::class, $requestListener); $apiTokenListener = new ApiTokenRequestListener($this->client->getToken()); $this->eventDispatcher->addListener(BeforeRequestEvent::class, $apiTokenListener); $acceptJsonListener = new AcceptJsonRequestListener(); $this->eventDispatcher->addListener(BeforeRequestEvent::class, $acceptJsonListener); $jsonContentTypeListener = new ContentTypeJsonRequestListener(); $this->eventDispatcher->addListener(BeforeRequestEvent::class, $jsonContentTypeListener); $userAgentListener = new UserAgentRequestListener(); $this->eventDispatcher->addListener(BeforeRequestEvent::class, $userAgentListener); $this->movieRepository = new MovieRepository($this->client); $this->tvRepository = new TvRepository($this->client); } public function popularMovies(int $page = 1, ?int $limit = null) { $movies = $this->movieRepository->getPopular(['page' => $page]); $movies = $movies->map(function ($movie) use ($movies) { return $this->parseResult($movies[$movie], "movie"); }); $movies = array_values($movies->toArray()); if (null !== $limit) { $movies = array_slice($movies, 0, $limit); } return $movies; } public function search(string $term, int $page = 1) { $searchRepository = new SearchRepository($this->client); $searchResults = $searchRepository->searchMulti($term, new KeywordSearchQuery(['page' => $page])); $results = []; foreach ($searchResults as $result) { if (!$result instanceof Movie && !$result instanceof Tv) { continue; } $results[] = $this->parseResult($result); } return $results; } public function find(string $id) { $finder = new Find($this->client); $result = $finder->findBy($id, ['external_source' => 'imdb_id']); return $this->parseResult($result); } public function movieDetails(string $id) { $client = new MovieRepository($this->client); $details = $client->getApi()->getMovie($id, ['append_to_response' => 'external_ids']); return $this->parseResult($details, "movie"); } public function tvDetails(string $id) { $client = new TvRepository($this->client); $details = $client->getApi()->getTvshow($id, ['append_to_response' => 'external_ids,seasons']); $details = $this->getEpisodesFromSeries($details); return $this->parseResult($details, "tvshow"); } public function episodeDetails(string $id, string $season, string $episode) { $client = new TvEpisodeRepository($this->client); $result = $client->getApi()->getEpisode($id, $season, $episode, ['append_to_response' => 'external_ids']); return $this->parseResult($result, "episode"); } public function getEpisodesFromSeries(array $series) { $client = new TvSeasonRepository($this->client); foreach ($series['seasons'] as $season) { if ($season['episode_count'] <= 0 || $season['name'] === 'Specials') { continue; } $series['episodes'][$season['season_number']] = $client->getApi()->getSeason($series['id'], $season['season_number'])['episodes']; } return $series; } public function mediaDetails(string $id, string $type) { if ($type === "movies") { return $this->movieDetails($id); } else { return $this->tvDetails($id); } } private function parseResult($result, $mediaType = null) { if (is_array($result)) { return $this->parseFromArray($result, $mediaType); } else { return $this->parseFromObject($result); } } private function parseFromArray($data, $mediaType) { if (null === $mediaType) { throw new \Exception("A media type must be set when parsing from an array."); } function parseTvShow(array $data, string $posterBasePath): TmdbResult { return new TmdbResult( imdbId: $data['external_ids']['imdb_id'], tmdbId: $data['id'], title: $data['name'], poster: $posterBasePath . $data['poster_path'], description: $data['overview'], year: (new \DateTime($data['first_air_date']))->format('Y'), mediaType: "tvshows", episodes: $data['episodes'], ); } function parseEpisode(array $data, string $posterBasePath): TmdbResult { return new TmdbResult( imdbId: $data['external_ids']['imdb_id'], tmdbId: $data['id'], title: $data['name'], poster: $posterBasePath . $data['still_path'], description: $data['overview'], year: (new \DateTime($data['air_date']))->format('Y'), mediaType: "tvshows", episodes: null, ); } function parseMovie(array $data, string $posterBasePath): TmdbResult { return new TmdbResult( imdbId: $data['external_ids']['imdb_id'], tmdbId: $data['id'], title: $data['title'], poster: $posterBasePath . $data['poster_path'], description: $data['overview'], year: (new \DateTime($data['release_date']))->format('Y'), mediaType: "movies", ); } if ($mediaType === 'movie') { $result = parseMovie($data, self::POSTER_IMG_PATH); } elseif ($mediaType === 'tvshow') { $result = parseTvShow($data, self::POSTER_IMG_PATH); } elseif ($mediaType === 'episode') { $result = parseEpisode($data, self::POSTER_IMG_PATH); } return $result; } private function parseFromObject($result): TmdbResult { $mediaType = $result instanceof Movie ? MediaType::Movie->value : MediaType::TvShow->value; $tmdbResult = new TmdbResult(); $tmdbResult->mediaType = $mediaType; $tmdbResult->tmdbId = $result->getId(); $tmdbResult->imdbId = $this->getImdbId($result->getId(), $mediaType); $tmdbResult->title = $this->getTitle($result, $mediaType); $tmdbResult->poster = self::POSTER_IMG_PATH . $result->getPosterImage(); $tmdbResult->year = $this->getReleaseDate($result, $mediaType); $tmdbResult->description = $result->getOverview(); return $tmdbResult; } public function getImdbId(string $tmdbId, $mediaType) { $externalIds = $this->cache->get("tmdb.externalIds.{$tmdbId}", function (ItemInterface $item) use ($tmdbId, $mediaType) { switch (MediaType::tryFrom($mediaType)->value) { case MediaType::Movie->value: return $this->movieRepository->getExternalIds($tmdbId); case MediaType::TvShow->value: return $this->tvRepository->getExternalIds($tmdbId); default: return null; } }); if (null === $externalIds) { return null; } return $externalIds->getImdbId() !== "" ? $externalIds->getImdbId() : "null"; } public function getImages($tmdbId, $mediaType) { return $this->cache->get("tmdb.images.{$tmdbId}", function (ItemInterface $item) use ($tmdbId, $mediaType) { switch (MediaType::tryFrom($mediaType)->value) { case MediaType::Movie->value: return $this->movieRepository->getImages($tmdbId); case MediaType::TvShow->value: return $this->tvRepository->getImages($tmdbId); default: return null; } }); } private function getReleaseDate($result, $mediaType): string { switch (MediaType::tryFrom($mediaType)->value) { case MediaType::Movie->value: return ($result->getReleaseDate() instanceof \DateTime) ? $result->getReleaseDate()->format('Y') : $result->getReleaseDate(); case MediaType::TvShow->value: return ($result->getFirstAirDate() instanceof \DateTime) ? $result->getFirstAirDate()->format('Y') : $result->getFirstAirDate(); default: return ""; } } private function getTitle($result, $mediaType): string { switch (MediaType::tryFrom($mediaType)->value) { case MediaType::Movie->value: return $result->getTitle(); case MediaType::TvShow->value: return $result->getName(); default: return ""; } } }