chore: tmdb client cleanup

This commit is contained in:
2025-09-02 16:37:26 -05:00
parent b8b71fa5b3
commit fc797a3a0f
12 changed files with 181 additions and 508 deletions

View File

@@ -4,12 +4,14 @@ namespace App\Tmdb;
use Aimeos\Map;
use App\Base\Enum\MediaType;
use App\Base\Util\ImdbMatcher;
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\ObjectMapper\ObjectMapper;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Contracts\Cache\ItemInterface;
use Tmdb\Api\Find;
use Tmdb\Client;
use Tmdb\Event\BeforeRequestEvent;
use Tmdb\Event\HydrationEvent;
@@ -22,22 +24,22 @@ use Tmdb\Event\RequestEvent;
use Tmdb\Model\Movie;
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 TmdbClient
{
protected Client $client;
protected MovieRepository $movieRepository;
protected TvRepository $tvRepository;
protected ObjectMapper $objectMapper;
const POSTER_IMG_PATH = "https://image.tmdb.org/t/p/w500";
protected Client $client;
protected MovieRepository $movieRepository;
protected TvRepository $tvRepository;
protected TvSeasonRepository $tvSeasonRepository;
protected SearchRepository $searchRepository;
public function __construct(
private readonly SerializerInterface $serializer,
private readonly CacheItemPoolInterface $cache,
@@ -62,12 +64,8 @@ class TmdbClient
'uri_factory' => null,
],
'hydration' => [
'event_listener_handles_hydration' => true,
'only_for_specified_models' => [
Movie::class,
Tv::class,
Tv\Episode::class,
]
'event_listener_handles_hydration' => false,
'only_for_specified_models' => []
]
]
);
@@ -96,54 +94,102 @@ class TmdbClient
$userAgentListener = new UserAgentRequestListener();
$this->eventDispatcher->addListener(BeforeRequestEvent::class, $userAgentListener);
// $hydrationListener = new HydrationListener($this->eventDispatcher);
// $this->eventDispatcher->addListener(HydrationEvent::class, $hydrationListener);
$this->movieRepository = new MovieRepository($this->client);
$this->tvRepository = new TvRepository($this->client);
$this->tvSeasonRepository = new TvSeasonRepository($this->client);
$this->searchRepository = new SearchRepository($this->client);
}
public function search() {}
public function search(string $term): TmdbResult|Map
{
if (ImdbMatcher::isMatch($term)) {
$handlers = [
'movie' => 'movieDetails',
'tvshow' => 'tvshowDetails',
];
$data = $this->findByImdbId($term);
$handler = $handlers[$data['media_type']];
return $this->$handler($term);
}
return $this->parseListOfResults(
$this->searchRepository->getApi()->searchMulti($term),
"movies"
);
}
public function find() {}
public function movieDetails(string $imdbId): ?TmdbResult
{
$tmdbId = $this->findByImdbId($imdbId)['id'];
return $this->parseResult(
$this->movieRepository->getApi()->getMovie($tmdbId, ['append_to_response' => 'external_ids']),
MediaType::Movie->value,
$imdbId
);
}
public function movieDetails() {}
public function tvshowDetails(string $imdbId): ?TmdbResult
{
$tmdbId = $this->findByImdbId($imdbId)['id'];
$media = $this->tvRepository->getApi()->getTvShow($tmdbId, ['append_to_response' => 'external_ids']);
$media['seasons'] = Map::from($media['seasons'])->filter(function ($data) {
return $data['season_number'] !== 0 &&
strtolower($data['name']) !== 'specials';
})->map(function ($data) use ($media) {
return $this->tvSeasonDetails($media['id'], $data['season_number'])['episodes'];
})->toArray();
public function tvshowDetails() {}
return $this->parseResult(
$media,
MediaType::TvShow->value,
$imdbId
);
}
public function tvEpisodeDetails() {}
public function tvSeasonDetails(string $tmdbId, int $season): array
{
$result = $this->tvSeasonRepository->getApi()->getSeason($tmdbId, $season);
$result['episodes'] = Map::from($result['episodes'])->map(function ($data) {
$data['still_path'] = self::POSTER_IMG_PATH . $data['still_path'];
$data['poster'] = $data['still_path'];
return $data;
})->toArray();
return $result;
}
public function relatedMedia() {}
public function relatedMedia(string $tmdbId, string $mediaType, int $resultCount = 6): Map
{
$repos = [
'movies' => $this->movieRepository,
'tvshows' => $this->tvRepository,
];
$results = $repos[$mediaType]->getApi()->getRecommendations($tmdbId);
return $this->parseListOfResults(
$results,
$mediaType,
$resultCount
);
}
public function popularMovies(int $resultCount = 6): Map
{
$results = $this->movieRepository->getApi()->getPopular();
return Map::from($results['results'])->map(function ($result) {
$result['external_ids'] = $this->getExternalIds($result['id'], MediaType::Movie->value);
return $result;
})->filter(function ($result) {
return array_key_exists('imdb_id', $result['external_ids']);
})->map(function ($result) {
$result = $this->serializer->denormalize($result, TmdbResult::class, context: ['media_type' => MediaType::Movie->value]);
return $result;
})->slice(0, $resultCount);
return $this->parseListOfResults(
$this->movieRepository->getApi()->getPopular(),
"movies",
$resultCount
);
}
public function popularTvShows(int $resultCount = 6): Map
{
$results = $this->tvRepository->getApi()->getPopular();
return Map::from($results['results'])->map(function ($result) {
$result['external_ids'] = $this->getExternalIds($result['id'], MediaType::Movie->value);
return $result;
})->filter(function ($result) {
return array_key_exists('imdb_id', $result['external_ids']) && $result['external_ids']['imdb_id'] !== null && $result['external_ids']['imdb_id'] !== "";
})->map(function ($result) {
$result = $this->serializer->denormalize($result, TmdbResult::class, context: ['media_type' => MediaType::TvShow->value]);
return $result;
})->slice(0, $resultCount);
return $this->parseListOfResults(
$this->tvRepository->getApi()->getPopular(),
"tvshows",
$resultCount
);
}
public function getExternalIds(string $tmdbId, $mediaType)
private function getExternalIds(string $tmdbId, $mediaType): ?array
{
try {
switch (MediaType::tryFrom($mediaType)->value) {
@@ -163,4 +209,51 @@ class TmdbClient
return $externalIds;
}
private function findByImdbId(string $imdbId): array
{
$finder = new Find($this->client);
$result = $finder->findBy($imdbId, ['external_source' => 'imdb_id']);
if (count($result['movie_results']) > 0) {
return $result['movie_results'][0];
} elseif (count($result['tv_results']) > 0) {
return $result['tv_results'][0];
} elseif (count($result['tv_episode_results']) > 0) {
return $result['tv_episode_results'][0];
}
throw new \Exception("No results found for $imdbId");
}
private function parseResult(array $data, string $mediaType, string $imdbId): TmdbResult
{
if (!array_key_exists('external_ids', $data)) {
$data['external_ids'] = ['imdb_id' => $imdbId];
}
return $this->serializer->denormalize($data, TmdbResult::class, context: ['media_type' => $mediaType]);
}
private function parseListOfResults(array $data, string $mediaType, ?int $resultCount = null): Map
{
$results = Map::from($data['results'])->filter(
fn ($result) => array_key_exists('id', $result)
)->map(function ($result) {
$result['external_ids'] = $this->getExternalIds($result['id'], MediaType::Movie->value);
return $result;
})->filter(function ($result) {
return array_key_exists('id', $result) &&
array_key_exists('imdb_id', $result['external_ids']) &&
$result['external_ids']['imdb_id'] !== null &&
$result['external_ids']['imdb_id'] !== "";
})->map(function ($result) use ($mediaType) {
return $this->serializer->denormalize($result, TmdbResult::class, context: ['media_type' => $mediaType]);
});
if (null !== $resultCount) {
$results = $results->slice(0, $resultCount);
}
return $results;
}
}