feat: search results
This commit is contained in:
8
src/Enum/MediaType.php
Normal file
8
src/Enum/MediaType.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enum;
|
||||
|
||||
enum MediaType
|
||||
{
|
||||
|
||||
}
|
||||
8
src/Search/Action/Command/SearchCommand.php
Normal file
8
src/Search/Action/Command/SearchCommand.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Search\Action\Command;
|
||||
|
||||
class SearchCommand
|
||||
{
|
||||
|
||||
}
|
||||
8
src/Search/Action/Handler/SearchHandler.php
Normal file
8
src/Search/Action/Handler/SearchHandler.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Search\Action\Handler;
|
||||
|
||||
class SearchHandler
|
||||
{
|
||||
|
||||
}
|
||||
8
src/Search/Action/Input/SearchInput.php
Normal file
8
src/Search/Action/Input/SearchInput.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Search\Action\Input;
|
||||
|
||||
class SearchInput
|
||||
{
|
||||
|
||||
}
|
||||
8
src/Search/Action/Result/SearchResult.php
Normal file
8
src/Search/Action/Result/SearchResult.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Search\Action\Result;
|
||||
|
||||
class SearchResult
|
||||
{
|
||||
|
||||
}
|
||||
275
src/Tmdb/Tmdb.php
Normal file
275
src/Tmdb/Tmdb.php
Normal file
@@ -0,0 +1,275 @@
|
||||
<?php
|
||||
|
||||
namespace App\Tmdb;
|
||||
|
||||
use App\ValueObject\MediaResult;
|
||||
use App\ValueObject\ResultFactory;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
use Symfony\Contracts\Cache\ItemInterface;
|
||||
use Tmdb\Api\Find;
|
||||
use Tmdb\Client;
|
||||
use Tmdb\Event\BeforeRequestEvent;
|
||||
use Tmdb\Event\Listener\Request\AcceptJsonRequestListener;
|
||||
use Tmdb\Event\Listener\Request\ApiTokenRequestListener;
|
||||
use Tmdb\Event\Listener\Request\ContentTypeJsonRequestListener;
|
||||
use Tmdb\Event\Listener\Request\UserAgentRequestListener;
|
||||
use Tmdb\Event\Listener\RequestListener;
|
||||
use Tmdb\Event\RequestEvent;
|
||||
use Tmdb\Model\AbstractModel;
|
||||
use Tmdb\Model\Image;
|
||||
use Tmdb\Model\Movie;
|
||||
use Tmdb\Model\Movie\QueryParameter\AppendToResponse;
|
||||
use Tmdb\Model\Search\SearchQuery\KeywordSearchQuery;
|
||||
use Tmdb\Model\Search\SearchQuery\MovieSearchQuery;
|
||||
use Tmdb\Model\Tv;
|
||||
use Tmdb\Repository\MovieRepository;
|
||||
use Tmdb\Repository\SearchRepository;
|
||||
use Tmdb\Repository\TvRepository;
|
||||
use Tmdb\Repository\TvSeasonRepository;
|
||||
use Tmdb\Token\Api\ApiToken;
|
||||
use Tmdb\Token\Api\BearerToken;
|
||||
|
||||
class Client
|
||||
{
|
||||
protected Client $client;
|
||||
|
||||
protected MovieRepository $movieRepository;
|
||||
|
||||
protected TvRepository $tvRepository;
|
||||
|
||||
const POSTER_IMG_PATH = "https://image.tmdb.org/t/p/w500";
|
||||
|
||||
public function __construct(
|
||||
private readonly CacheInterface $cache,
|
||||
private readonly EventDispatcherInterface $eventDispatcher,
|
||||
#[Autowire(env: 'TMDB_API')] string $apiKey,
|
||||
) {
|
||||
$this->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)
|
||||
{
|
||||
$movies = $this->movieRepository->getPopular(['page' => $page]);
|
||||
|
||||
foreach ($movies as $movie) {
|
||||
$this->parseResult($movie);
|
||||
}
|
||||
|
||||
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, "tv");
|
||||
}
|
||||
|
||||
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): MediaResult {
|
||||
return new MediaResult(
|
||||
imdbId: $data['external_ids']['imdb_id'],
|
||||
tvdbId: $data['id'],
|
||||
title: $data['name'],
|
||||
poster: $posterBasePath . $data['poster_path'],
|
||||
excerpt: $data['overview'],
|
||||
year: (new \DateTime($data['first_air_date']))->format('Y'),
|
||||
mediaType: "tvshows",
|
||||
episodes: $data['episodes'],
|
||||
);
|
||||
}
|
||||
|
||||
function parseMovie(array $data, string $posterBasePath): MediaResult {
|
||||
return new MediaResult(
|
||||
imdbId: $data['external_ids']['imdb_id'],
|
||||
tvdbId: $data['id'],
|
||||
title: $data['title'],
|
||||
poster: $posterBasePath . $data['poster_path'],
|
||||
excerpt: $data['overview'],
|
||||
year: (new \DateTime($data['release_date']))->format('Y'),
|
||||
mediaType: "movies",
|
||||
);
|
||||
}
|
||||
|
||||
$posterBasePath = self::POSTER_IMG_PATH;
|
||||
$result = ("movie" === $mediaType) ? parseMovie($data, $posterBasePath) : parseTvShow($data, $posterBasePath);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function parseFromObject($result)
|
||||
{
|
||||
$result->mediaType = $result instanceof Movie ? 'movies' : 'tvshows';
|
||||
|
||||
$externalIds = $this->cache->get("tmdb.externalIds.{$result->getId()}", function (ItemInterface $item) use ($result) {
|
||||
if ($result instanceof Movie) {
|
||||
$externalIds = $this->movieRepository->getExternalIds($result->getId());
|
||||
} else {
|
||||
$externalIds = $this->tvRepository->getExternalIds($result->getId());
|
||||
}
|
||||
return $externalIds;
|
||||
});
|
||||
|
||||
$images = $this->cache->get("tmdb.images.{$result->getId()}", function (ItemInterface $item) use ($result) {
|
||||
if ($result instanceof Movie) {
|
||||
$images = $this->movieRepository->getImages($result->getId());
|
||||
} else {
|
||||
$images = $this->tvRepository->getImages($result->getId());
|
||||
}
|
||||
return $images;
|
||||
});
|
||||
|
||||
if (null !== $externalIds) {
|
||||
$imdbId = $externalIds->getImdbId() !== "" ? $externalIds->getImdbId() : "null";
|
||||
|
||||
if ("movies" === $result->mediaType) {
|
||||
$result->setImdbId($imdbId);
|
||||
} else {
|
||||
$result->imdbId = $imdbId;
|
||||
$result->title = $result->getName();
|
||||
}
|
||||
} else {
|
||||
if ("movies" === $result->mediaType) {
|
||||
$result->setImdbId("null");
|
||||
} else {
|
||||
$result->imdbId = "null";
|
||||
}
|
||||
}
|
||||
|
||||
if ("movies" === $result->mediaType) {
|
||||
if ($result->getReleaseDate() instanceof \DateTime) {
|
||||
$result->year = $result->getReleaseDate()->format("Y");
|
||||
} else {
|
||||
$result->year = (new \DateTime($result->getReleaseDate()))->format('Y');
|
||||
}
|
||||
} else {
|
||||
if ($result->getFirstAirDate() instanceof \DateTime) {
|
||||
$result->year = $result->getFirstAirDate()->format("Y");
|
||||
} else {
|
||||
$result->year = (new \DateTime($result->getFirstAirDate()))->format('Y');
|
||||
}
|
||||
}
|
||||
|
||||
/** @var Movie $result */
|
||||
$result->setExternalIds($externalIds);
|
||||
|
||||
$result->setImages($images);
|
||||
$result->getPosterImage()->setFilePath(
|
||||
self::POSTER_IMG_PATH . $result->getPosterImage()->getFilePath()
|
||||
);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
17
src/Tmdb/TmdbResult.php
Normal file
17
src/Tmdb/TmdbResult.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\ValueObject;
|
||||
|
||||
class MediaResult
|
||||
{
|
||||
public function __construct(
|
||||
public string $imdbId,
|
||||
public string $tvdbId,
|
||||
public string $title,
|
||||
public string $poster,
|
||||
public string $excerpt,
|
||||
public string $year,
|
||||
public string $mediaType,
|
||||
public ?array $episodes = null,
|
||||
) {}
|
||||
}
|
||||
Reference in New Issue
Block a user