From b42924048f2e250a6cbc0d80fe4fec96a4225585 Mon Sep 17 00:00:00 2001 From: Brock H Caldwell Date: Mon, 8 Sep 2025 14:20:33 -0500 Subject: [PATCH] chore: makes better use of symfony denormalizer --- composer.json | 5 + composer.lock | 57 +-------- config/services.yaml | 2 - src/Tmdb/Dto/CastMemberDto.php | 19 +++ src/Tmdb/Dto/CrewMemberDto.php | 18 +++ src/Tmdb/Dto/GenreDto.php | 18 +++ src/Tmdb/Dto/TmdbEpisodeDto.php | 29 +++++ .../TmdbMovieResultDenormalizer.php | 64 ++++++++++ .../Serializer/TmdbResultDenormalizer.php | 78 ++++++++++++ .../TmdbTvEpisodeResultDenormalizer.php | 42 +++++++ .../TmdbTvShowResultDenormalizer.php | 55 +++++++++ src/Tmdb/TmdbClient.php | 93 ++++++++------- src/Tmdb/TmdbResult.php | 29 +++++ src/Tmdb/TmdbResultDenormalizer.php | 111 ------------------ .../Action/Result/GetTvShowOptionsResult.php | 5 +- templates/components/TvEpisodeList.html.twig | 38 +++--- templates/search/result.html.twig | 23 +++- 17 files changed, 449 insertions(+), 237 deletions(-) create mode 100644 src/Tmdb/Dto/CastMemberDto.php create mode 100644 src/Tmdb/Dto/CrewMemberDto.php create mode 100644 src/Tmdb/Dto/GenreDto.php create mode 100644 src/Tmdb/Dto/TmdbEpisodeDto.php create mode 100644 src/Tmdb/Framework/Serializer/TmdbMovieResultDenormalizer.php create mode 100644 src/Tmdb/Framework/Serializer/TmdbResultDenormalizer.php create mode 100644 src/Tmdb/Framework/Serializer/TmdbTvEpisodeResultDenormalizer.php create mode 100644 src/Tmdb/Framework/Serializer/TmdbTvShowResultDenormalizer.php delete mode 100644 src/Tmdb/TmdbResultDenormalizer.php diff --git a/composer.json b/composer.json index 349015f..f86c863 100644 --- a/composer.json +++ b/composer.json @@ -25,6 +25,8 @@ "p3k/emoji-detector": "^1.2", "php-http/cache-plugin": "^2.0", "php-tmdb/api": "^4.1", + "phpdocumentor/reflection-docblock": "^5.6", + "phpstan/phpdoc-parser": "^2.1", "predis/predis": "^2.4", "runtime/frankenphp-symfony": "^0.2.0", "spatie/icalendar-generator": "^3.0", @@ -47,9 +49,12 @@ "symfony/notifier": "7.3.*", "symfony/ntfy-notifier": "7.3.*", "symfony/object-mapper": "7.3.*", + "symfony/property-access": "7.3.*", + "symfony/property-info": "7.3.*", "symfony/runtime": "7.3.*", "symfony/scheduler": "7.3.*", "symfony/security-bundle": "7.3.*", + "symfony/serializer": "7.3.*", "symfony/stimulus-bundle": "^2.24", "symfony/twig-bundle": "7.3.*", "symfony/ux-autocomplete": "^2.27", diff --git a/composer.lock b/composer.lock index 00212c7..3098671 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e055bbbbe5836c92bb147b6dbb1d1d46", + "content-hash": "c133ccd27ac6a41256bdc69129c16546", "packages": [ { "name": "1tomany/rich-bundle", @@ -9467,57 +9467,6 @@ ], "time": "2025-05-12T14:48:23+00:00" }, - { - "name": "symfony/serializer-pack", - "version": "v1.3.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/serializer-pack.git", - "reference": "2844d81a5fc86b617b82f44a8bfcaaba1d583eee" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/serializer-pack/zipball/2844d81a5fc86b617b82f44a8bfcaaba1d583eee", - "reference": "2844d81a5fc86b617b82f44a8bfcaaba1d583eee", - "shasum": "" - }, - "require": { - "phpdocumentor/reflection-docblock": "*", - "phpstan/phpdoc-parser": "*", - "symfony/property-access": "*", - "symfony/property-info": "*", - "symfony/serializer": "*" - }, - "conflict": { - "symfony/property-info": "<5.4", - "symfony/serializer": "<5.4" - }, - "type": "symfony-pack", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A pack for the Symfony serializer", - "support": { - "issues": "https://github.com/symfony/serializer-pack/issues", - "source": "https://github.com/symfony/serializer-pack/tree/v1.3.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-06-03T13:55:25+00:00" - }, { "name": "symfony/service-contracts", "version": "v3.6.0", @@ -13398,7 +13347,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": [], "prefer-stable": true, "prefer-lowest": false, "platform": { @@ -13406,7 +13355,7 @@ "ext-ctype": "*", "ext-iconv": "*" }, - "platform-dev": {}, + "platform-dev": [], "platform-overrides": { "php": "8.4" }, diff --git a/config/services.yaml b/config/services.yaml index 7c744aa..4d9d2dc 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -67,8 +67,6 @@ services: # please note that last definitions always *replace* previous ones App\Download\Downloader\DownloaderInterface: "@App\\Download\\Downloader\\ProcessDownloader" - - # Session Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler: arguments: diff --git a/src/Tmdb/Dto/CastMemberDto.php b/src/Tmdb/Dto/CastMemberDto.php new file mode 100644 index 0000000..bd4b244 --- /dev/null +++ b/src/Tmdb/Dto/CastMemberDto.php @@ -0,0 +1,19 @@ +name; + } +} diff --git a/src/Tmdb/Dto/CrewMemberDto.php b/src/Tmdb/Dto/CrewMemberDto.php new file mode 100644 index 0000000..7643ac1 --- /dev/null +++ b/src/Tmdb/Dto/CrewMemberDto.php @@ -0,0 +1,18 @@ +name; + } +} diff --git a/src/Tmdb/Dto/GenreDto.php b/src/Tmdb/Dto/GenreDto.php new file mode 100644 index 0000000..9b20211 --- /dev/null +++ b/src/Tmdb/Dto/GenreDto.php @@ -0,0 +1,18 @@ +name; + } +} diff --git a/src/Tmdb/Dto/TmdbEpisodeDto.php b/src/Tmdb/Dto/TmdbEpisodeDto.php new file mode 100644 index 0000000..3ca23f1 --- /dev/null +++ b/src/Tmdb/Dto/TmdbEpisodeDto.php @@ -0,0 +1,29 @@ +value, + public ?string $imdbId = null, + public ?string $name = null, + #[SerializedPath('[air_date]')] + public ?string $airDate = null, + #[SerializedPath('[overview]')] + public ?string $description = null, + public ?string $poster = null, + public ?int $runtime = 0, + #[SerializedPath('[season_number]')] + public ?int $seasonNumber = 0, + #[SerializedPath('[episode_number]')] + public ?int $episodeNumber = 0, + ) {} +} diff --git a/src/Tmdb/Framework/Serializer/TmdbMovieResultDenormalizer.php b/src/Tmdb/Framework/Serializer/TmdbMovieResultDenormalizer.php new file mode 100644 index 0000000..9d936cc --- /dev/null +++ b/src/Tmdb/Framework/Serializer/TmdbMovieResultDenormalizer.php @@ -0,0 +1,64 @@ +normalizer->denormalize($data, TmdbResult::class, $format, $context); + + if (array_key_exists('release_date', $data) && !in_array($data['release_date'], ['', null,])) { + $airDate = (new \DateTime($data['release_date'])); + } else { + $airDate = null; + } + + if (!array_key_exists('original_title', $data)) { + dd($data, $context); + } + + $result->title = $data['original_title']; + $result->premiereDate = $airDate; + $result->poster = (null !== $data['poster_path']) ? self::POSTER_IMG_PATH . $data['poster_path'] : null; + $result->year = (null !== $airDate) ? $airDate->format('Y') : null; + $result->mediaType = "movies"; + $result->stars = $this->getStars($data); + $result->directors = $this->getDirectors($data); + dd($result); + return $result; + } + + public function supportsDenormalization( + mixed $data, + string $type, + ?string $format = null, + array $context = [] + ): bool { + return array_key_exists('media_type', $context) && + $context['media_type'] === MediaType::Movie->value; + } + + public function getSupportedTypes(?string $format): array + { + return [ + TmdbResult::class => false, + ]; + } +} diff --git a/src/Tmdb/Framework/Serializer/TmdbResultDenormalizer.php b/src/Tmdb/Framework/Serializer/TmdbResultDenormalizer.php new file mode 100644 index 0000000..75b8fd0 --- /dev/null +++ b/src/Tmdb/Framework/Serializer/TmdbResultDenormalizer.php @@ -0,0 +1,78 @@ +normalizer->denormalize($data, TmdbResult::class, $format, $context); + } + + protected function getStars(array $data): ?array + { + if (!array_key_exists('credits', $data)) { + return null; + } + return Map::from($data['credits']['cast']) + ->slice(0, 3) + ->map(fn($item) => $this->normalizer->denormalize($item, CastMemberDto::class)) + ->toArray(); + } + + protected function getDirectors(array $data): ?array + { + if (!array_key_exists('credits', $data)) { + return null; + } + return Map::from($data['credits']['crew']) + ->filter(fn($item) => $item['job'] === 'Director') + ->slice(0, 3) + ->map(fn($item) => $this->normalizer->denormalize($item, CrewMemberDto::class)) + ->toArray(); + } + + public function getGenres(array $data, MediaType $mediaType): ?array + { + if (array_key_exists('genres', $data)) { + return null; + } + + return Map::from($data['genres']) + ->map(fn($item) => $this->normalizer->denormalize($item, GenreDto::class)) + ->toArray(); + } + + public function supportsDenormalization( + mixed $data, + string $type, + ?string $format = null, + array $context = [] + ): bool { + return !array_key_exists('media_type', $context); + } + + public function getSupportedTypes(?string $format): array + { + return [ + TmdbResult::class => false, + ]; + } +} diff --git a/src/Tmdb/Framework/Serializer/TmdbTvEpisodeResultDenormalizer.php b/src/Tmdb/Framework/Serializer/TmdbTvEpisodeResultDenormalizer.php new file mode 100644 index 0000000..ce4bca4 --- /dev/null +++ b/src/Tmdb/Framework/Serializer/TmdbTvEpisodeResultDenormalizer.php @@ -0,0 +1,42 @@ +normalizer->denormalize($data, TmdbResult::class, $format, $context); + return $result; + } + + public function supportsDenormalization( + mixed $data, + string $type, + ?string $format = null, + array $context = [] + ): bool { + return array_key_exists('media_type', $context) && + $context['media_type'] === MediaType::TvEpisode->value; + } + + public function getSupportedTypes(?string $format): array + { + return [ + TmdbResult::class => false, + ]; + } +} diff --git a/src/Tmdb/Framework/Serializer/TmdbTvShowResultDenormalizer.php b/src/Tmdb/Framework/Serializer/TmdbTvShowResultDenormalizer.php new file mode 100644 index 0000000..06a9157 --- /dev/null +++ b/src/Tmdb/Framework/Serializer/TmdbTvShowResultDenormalizer.php @@ -0,0 +1,55 @@ +normalizer->denormalize($data, TmdbResult::class, $format, $context); + + if (!in_array($data['first_air_date'], ['', null,])) { + $airDate = (new \DateTime($data['first_air_date'])); + } else { + $airDate = null; + } + + $result->title = $data['original_name']; + $result->premiereDate = $airDate; + $result->poster = (null !== $data['poster_path']) ? self::POSTER_IMG_PATH . $data['poster_path'] : null; + $result->year = (null !== $airDate) ? $airDate->format('Y') : null; + $result->mediaType = MediaType::TvShow->value; + return $result; + } + + public function supportsDenormalization( + mixed $data, + string $type, + ?string $format = null, + array $context = [] + ): bool { + return array_key_exists('media_type', $context) && + $context['media_type'] === "tvshows"; + } + + public function getSupportedTypes(?string $format): array + { + return [ + TmdbResult::class => false, + ]; + } +} diff --git a/src/Tmdb/TmdbClient.php b/src/Tmdb/TmdbClient.php index e8cce99..5ea71c3 100644 --- a/src/Tmdb/TmdbClient.php +++ b/src/Tmdb/TmdbClient.php @@ -5,6 +5,7 @@ namespace App\Tmdb; use Aimeos\Map; use App\Base\Enum\MediaType; use App\Base\Util\ImdbMatcher; +use App\Tmdb\Dto\TmdbEpisodeDto; use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\DependencyInjection\Attribute\Autowire; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -37,6 +38,16 @@ class TmdbClient protected TvEpisodeRepository $tvEpisodeRepository; protected SearchRepository $searchRepository; + protected array $mediaTypeMap = [ + MediaType::Movie->value => MediaType::Movie->value, + MediaType::TvShow->value => MediaType::TvShow->value, + MediaType::TvEpisode->value => MediaType::TvEpisode->value, + 'movie' => 'movies', + 'tv' => 'tvshows', + ]; + + protected $repos = []; + public function __construct( private readonly SerializerInterface $serializer, private readonly CacheItemPoolInterface $cache, @@ -96,6 +107,11 @@ class TmdbClient $this->tvSeasonRepository = new TvSeasonRepository($this->client); $this->tvEpisodeRepository = new TvEpisodeRepository($this->client); $this->searchRepository = new SearchRepository($this->client); + $this->repos = [ + MediaType::Movie->value => $this->movieRepository, + MediaType::TvShow->value => $this->tvRepository, + MediaType::TvEpisode->value => $this->tvEpisodeRepository, + ]; } public function search(string $term): TmdbResult|Map @@ -109,17 +125,15 @@ class TmdbClient $handler = $handlers[$data['media_type']]; return $this->$handler($term); } - return $this->parseListOfResults( - $this->searchRepository->getApi()->searchMulti($term), - "movies" - ); + $results = $this->searchRepository->getApi()->searchMulti($term); + return $this->parseListOfResults($results); } public function movieDetails(string $imdbId): ?TmdbResult { $tmdbId = $this->findByImdbId($imdbId)['id']; return $this->parseResult( - $this->movieRepository->getApi()->getMovie($tmdbId, ['append_to_response' => 'external_ids']), + $this->movieRepository->getApi()->getMovie($tmdbId, ['append_to_response' => 'external_ids,credits']), MediaType::Movie->value, $imdbId ); @@ -128,7 +142,7 @@ class TmdbClient public function tvshowDetails(string $imdbId): ?TmdbResult { $tmdbId = $this->findByImdbId($imdbId)['id']; - $media = $this->tvRepository->getApi()->getTvShow($tmdbId, ['append_to_response' => 'external_ids']); + $media = $this->tvRepository->getApi()->getTvShow($tmdbId, ['append_to_response' => 'external_ids,credits']); $media['seasons'] = Map::from($media['seasons'])->filter(function ($data) { return $data['season_number'] !== 0 && strtolower($data['name']) !== 'specials'; @@ -147,7 +161,7 @@ class TmdbClient public function tvSeasonDetails(string $tmdbId, int $season): array { - $result = $this->tvSeasonRepository->getApi()->getSeason($tmdbId, $season); + $result = $this->tvSeasonRepository->getApi()->getSeason($tmdbId, $season, ['append_to_response' => 'external_ids,credits']); $result['episodes'] = Map::from($result['episodes'])->map(function ($data) { $data['still_path'] = self::POSTER_IMG_PATH . $data['still_path']; $data['poster'] = $data['still_path']; @@ -156,9 +170,9 @@ class TmdbClient return $result; } - public function tvEpisodeDetails(string $tmdbId, int $season, int $episode): ?TmdbResult + public function tvEpisodeDetails(string $tmdbId, int $season, int $episode): TmdbResult|TmdbEpisodeDto|null { - $result = $this->tvEpisodeRepository->getApi()->getEpisode($tmdbId, $season, $episode, ['append_to_response' => 'external_ids']); + $result = $this->tvEpisodeRepository->getApi()->getEpisode($tmdbId, $season, $episode, ['append_to_response' => 'external_ids,credits']); return $this->parseResult( $result, MediaType::TvEpisode->value, @@ -168,56 +182,46 @@ class TmdbClient public function relatedMedia(string $tmdbId, string $mediaType, int $resultCount = 6): Map { - $repos = [ - 'movies' => $this->movieRepository, - 'tvshows' => $this->tvRepository, - ]; - $results = $repos[$mediaType]->getApi()->getRecommendations($tmdbId); - + $results = $this->repos[$mediaType]->getApi()->getRecommendations($tmdbId, ['append_to_response' => 'external_ids']); return $this->parseListOfResults( $results, - $mediaType, $resultCount ); } public function popularMovies(int $resultCount = 6): Map { + $results = $this->movieRepository->getApi()->getPopular(); + $results['results'] = Map::from($results['results'])->map(function ($result) { + $result['media_type'] = MediaType::Movie->value; + return $result; + }); return $this->parseListOfResults( - $this->movieRepository->getApi()->getPopular(), - "movies", + $results, $resultCount ); } public function popularTvShows(int $resultCount = 6): Map { + $results = $this->tvRepository->getApi()->getPopular(); + $results['results'] = Map::from($results['results'])->map(function ($result) { + $result['media_type'] = MediaType::TvShow->value; + return $result; + }); return $this->parseListOfResults( - $this->tvRepository->getApi()->getPopular(), - "tvshows", + $results, $resultCount ); } - private function getExternalIds(string $tmdbId, $mediaType): ?array + private function getExternalIds(int $tmdbId, string $mediaType): ?array { - try { - switch (MediaType::tryFrom($mediaType)->value) { - case MediaType::Movie->value: - $externalIds = $this->movieRepository->getApi()->getExternalIds($tmdbId); - break; - case MediaType::TvShow->value: - $externalIds = $this->tvRepository->getApi()->getExternalIds($tmdbId); - break; - default: - $externalIds = null; - break; - } - } catch (\throwable $e) { + if (!array_key_exists($mediaType, $this->repos) || + !in_array($mediaType, [MediaType::Movie->value, MediaType::TvShow->value])) { return []; } - - return $externalIds; + return $this->repos[$mediaType]->getApi()->getExternalIds($tmdbId); } private function findByImdbId(string $imdbId): array @@ -236,7 +240,7 @@ class TmdbClient throw new \Exception("No results found for $imdbId"); } - private function parseResult(array $data, string $mediaType, string $imdbId): TmdbResult + private function parseResult(array $data, string $mediaType, string $imdbId): TmdbResult|TmdbEpisodeDto { if (!array_key_exists('external_ids', $data)) { $data['external_ids'] = ['imdb_id' => $imdbId]; @@ -244,20 +248,21 @@ class TmdbClient return $this->serializer->denormalize($data, TmdbResult::class, context: ['media_type' => $mediaType]); } - private function parseListOfResults(array $data, string $mediaType, ?int $resultCount = null): Map + private function parseListOfResults(array $data, ?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); + $results = Map::from($data['results'])->filter(function ($result) { + return array_key_exists('media_type', $result) && + in_array($result['media_type'], array_keys($this->mediaTypeMap)); + })->map(function ($result) { + $result['external_ids'] = $this->getExternalIds($result['id'], $this->mediaTypeMap[$result['media_type']]); 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]); + })->map(function ($result) { + return $this->serializer->denormalize($result, TmdbResult::class, context: ['media_type' => $this->mediaTypeMap[$result['media_type']]]); }); if (null !== $resultCount) { diff --git a/src/Tmdb/TmdbResult.php b/src/Tmdb/TmdbResult.php index 14861cc..4c47d15 100644 --- a/src/Tmdb/TmdbResult.php +++ b/src/Tmdb/TmdbResult.php @@ -2,10 +2,32 @@ namespace App\Tmdb; +use App\Base\Enum\MediaType; +use App\Tmdb\Dto\CastMemberDto; +use App\Tmdb\Dto\CrewMemberDto; +use App\Tmdb\Dto\GenreDto; +use App\Tmdb\Dto\TmdbEpisodeDto; +use Symfony\Component\Serializer\Attribute\Context; use Symfony\Component\Serializer\Attribute\SerializedPath; class TmdbResult { + /** + * @param string|null $imdbId + * @param int|null $tmdbId + * @param string|null $title + * @param string|null $description + * @param string|null $poster + * @param \DateTimeInterface|null $premiereDate + * @param string|null $year + * @param string|null $mediaType + * @param array|null $episodes + * @param string|null $episodeAirDate + * @param GenreDto[]|null $genres + * @param CastMemberDto[]|null $stars + * @param CrewMemberDto[]|null $directors + * @param int|null $runtime + */ public function __construct( #[SerializedPath('[external_ids][imdb_id]')] public ?string $imdbId = "", @@ -18,8 +40,15 @@ class TmdbResult public ?\DateTimeInterface $premiereDate = null, public ?string $year = null, public ?string $mediaType = "", + #[Context(denormalizationContext: [ + 'media_type' => MediaType::TvEpisode->value + ])] #[SerializedPath('[seasons]')] public ?array $episodes = null, public ?string $episodeAirDate = null, + public ?array $genres = null, + public ?array $stars = null, + public ?array $directors = null, + public ?int $runtime = null, ) {} } diff --git a/src/Tmdb/TmdbResultDenormalizer.php b/src/Tmdb/TmdbResultDenormalizer.php deleted file mode 100644 index 5348c59..0000000 --- a/src/Tmdb/TmdbResultDenormalizer.php +++ /dev/null @@ -1,111 +0,0 @@ -value => 'parseMovie', - MediaType::TvShow->value => 'parseTvShow', - 'movie' => 'parseMovie', - 'tv' => 'parseTvShow', - 'tvepisode' => 'parseEpisode', - ]; - - $denormalized = $this->normalizer->denormalize($data, TmdbResult::class, $format, $context); - $parser = array_key_exists('media_type', $data) ? $parsers[$data['media_type']] : $parsers[$context['media_type']]; - return $this->$parser($data, $denormalized); - } - - private function parseTvShow(array $data, TmdbResult $result): TmdbResult - { - if (!in_array($data['first_air_date'], ['', null,])) { - $airDate = (new \DateTime($data['first_air_date'])); - } else { - $airDate = null; - } - - $result->title = $data['original_name']; - $result->premiereDate = $airDate; - $result->poster = (null !== $data['poster_path']) ? self::POSTER_IMG_PATH . $data['poster_path'] : null; - $result->year = (null !== $airDate) ? $airDate->format('Y') : null; - $result->mediaType = "tvshows"; - - return $result; - } - - private function parseMovie(array $data, TmdbResult $result): TmdbResult - { - if (!in_array($data['release_date'], ['', null,])) { - $airDate = (new \DateTime($data['release_date'])); - } else { - $airDate = null; - } - - $result->title = $data['original_title']; - $result->premiereDate = $airDate; - $result->poster = (null !== $data['poster_path']) ? self::POSTER_IMG_PATH . $data['poster_path'] : null; - $result->year = (null !== $airDate) ? $airDate->format('Y') : null; - $result->mediaType = "movies"; - - return $result; - } - - private function parseEpisode(array $data, TmdbResult $result): TmdbResult - { - if (!in_array($data['air_date'], ['', null,])) { - $airDate = (new \DateTime($data['air_date'])); - } else { - $airDate = null; - } - - $result->premiereDate = $airDate; - $result->episodeAirDate = $airDate->format('m/d/Y'); - $result->poster = (null !== $data['still_path']) ? self::POSTER_IMG_PATH . $data['still_path'] : null; - $result->year = (null !== $airDate) ? $airDate->format('Y') : null; - $result->mediaType = "tvshows"; - - return $result; - } - - /** - * @inheritDoc - */ - public function supportsDenormalization( - mixed $data, - string $type, - ?string $format = null, - array $context = [] - ): bool { - return $type === TmdbResult::class; - } - - /** - * @inheritDoc - */ - public function getSupportedTypes(?string $format): array - { - return [ - TmdbResult::class => true, - ]; - } -} \ No newline at end of file diff --git a/src/Torrentio/Action/Result/GetTvShowOptionsResult.php b/src/Torrentio/Action/Result/GetTvShowOptionsResult.php index 5a4cc1f..92d88b2 100644 --- a/src/Torrentio/Action/Result/GetTvShowOptionsResult.php +++ b/src/Torrentio/Action/Result/GetTvShowOptionsResult.php @@ -3,14 +3,15 @@ namespace App\Torrentio\Action\Result; use App\Library\Dto\MediaFileDto; +use App\Tmdb\Dto\TmdbEpisodeDto; use App\Tmdb\TmdbResult; use OneToMany\RichBundle\Contract\ResultInterface; class GetTvShowOptionsResult implements ResultInterface { public function __construct( - public TmdbResult $parentShow, - public TmdbResult $media, + public TmdbResult|TmdbEpisodeDto $parentShow, + public TmdbResult|TmdbEpisodeDto $media, public MediaFileDto|false $file, public string $season, public string $episode, diff --git a/templates/components/TvEpisodeList.html.twig b/templates/components/TvEpisodeList.html.twig index 3ee3ce4..112861e 100644 --- a/templates/components/TvEpisodeList.html.twig +++ b/templates/components/TvEpisodeList.html.twig @@ -3,7 +3,7 @@ >
{% for episode in this.getEpisodes().items %} -
- {% if episode['poster'] != null %} - + {% if episode.poster != null %} + {% else %}
@@ -27,24 +27,24 @@ {% endif %}

- {{ episode['episode_number'] }}. {{ episode['name'] }} + {{ episode.episodeNumber }}. {{ episode.name }}

-

{{ episode['overview']|truncate }}

+

{{ episode.description|truncate }}

- - - {{ episode['air_date']|date(null, 'UTC') }} + + {{ episode.airDate|date(null, 'UTC') }} - missing @@ -58,7 +58,7 @@ {{ stimulus_target('tv-results', 'episodeSelector') }} />
-
diff --git a/templates/search/result.html.twig b/templates/search/result.html.twig index 36cefc4..f06e57e 100644 --- a/templates/search/result.html.twig +++ b/templates/search/result.html.twig @@ -20,7 +20,7 @@

- {{ results.media.title }} ({{ results.media.year|date('Y') }}) + {{ results.media.title }} ({{ results.media.episodeAirDate|date('Y') }})

{% if results.media.mediaType == "tvshows" %} @@ -53,6 +53,15 @@

{{ results.media.description }} +

+ + {% if results.media.stars != null %} + Starring: {{ results.media.stars|join(', ') }}
+ {% endif %} + + {% if results.media.directors != null %} + Directors: {{ results.media.directors|join(', ') }}
+ {% endif %}

{% if "movies" == results.media.mediaType %} @@ -61,10 +70,6 @@ - results - - {{ results.media.episodeAirDate|date(null, 'UTC') }} - - + {{ results.media.episodeAirDate|date(null, 'UTC') }} +
+ + + {{ results.media.runtime }} minutes +
{% endif %}