From c0f14730373a46e71f7417357a67bb4efc2421a8 Mon Sep 17 00:00:00 2001 From: Brock H Caldwell Date: Fri, 5 Sep 2025 15:43:01 -0500 Subject: [PATCH] wip: mostly working tmdb client --- .../Framework/Controller/IndexController.php | 4 - .../Action/Handler/DownloadSeasonHandler.php | 8 +- .../Handler/MonitorTvEpisodeHandler.php | 4 +- .../Action/Handler/MonitorTvSeasonHandler.php | 6 +- .../Action/Handler/MonitorTvShowHandler.php | 7 +- .../Action/Handler/GetMediaInfoHandler.php | 2 - src/Search/Action/Handler/SearchHandler.php | 2 - .../Framework/Controller/ApiController.php | 3 +- src/Tmdb/HydrationListener.php | 14 ---- src/Tmdb/TmdbClient.php | 22 +++-- src/Tmdb/TmdbResultDenormalizer.php | 4 +- .../Action/Handler/GetMovieOptionsHandler.php | 6 +- .../Handler/GetTvShowOptionsHandler.php | 9 ++- src/Twig/Components/UpcomingEpisodes.php | 81 ------------------- tests/Monitor/MonitorTvShowHandlerTest.php | 6 +- 15 files changed, 43 insertions(+), 135 deletions(-) delete mode 100644 src/Tmdb/HydrationListener.php delete mode 100644 src/Twig/Components/UpcomingEpisodes.php diff --git a/src/Base/Framework/Controller/IndexController.php b/src/Base/Framework/Controller/IndexController.php index b1a8598..928d3a7 100644 --- a/src/Base/Framework/Controller/IndexController.php +++ b/src/Base/Framework/Controller/IndexController.php @@ -2,11 +2,8 @@ namespace App\Base\Framework\Controller; -use App\Monitor\Action\Command\MonitorTvShowCommand; use App\Monitor\Action\Handler\MonitorTvShowHandler; -use App\Tmdb\Tmdb; use App\Tmdb\TmdbClient; -use App\User\Framework\Entity\User; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -18,7 +15,6 @@ final class IndexController extends AbstractController { public function __construct( private readonly TmdbClient $tmdb, - private readonly MonitorTvShowHandler $monitorTvShowHandler, ) {} #[Route('/', name: 'app_index')] diff --git a/src/Download/Action/Handler/DownloadSeasonHandler.php b/src/Download/Action/Handler/DownloadSeasonHandler.php index 63f6552..7ca722b 100644 --- a/src/Download/Action/Handler/DownloadSeasonHandler.php +++ b/src/Download/Action/Handler/DownloadSeasonHandler.php @@ -3,13 +3,14 @@ namespace App\Download\Action\Handler; use Aimeos\Map; +use App\Base\Enum\MediaType; use App\Base\Service\MediaFiles; use App\Download\Action\Command\DownloadMediaCommand; use App\Download\Action\Command\DownloadSeasonCommand; use App\Download\Action\Result\DownloadMediaResult; use App\Download\Action\Result\DownloadSeasonResult; use App\Download\DownloadOptionEvaluator; -use App\Tmdb\Tmdb; +use App\Tmdb\TmdbClient; use App\Torrentio\Action\Command\GetTvShowOptionsCommand; use App\Torrentio\Action\Handler\GetTvShowOptionsHandler; use App\User\Dto\UserPreferencesFactory; @@ -27,7 +28,7 @@ readonly class DownloadSeasonHandler implements HandlerInterface public function __construct( private MediaFiles $mediaFiles, private LoggerInterface $logger, - private Tmdb $tmdb, + private TmdbClient $tmdb, private MessageBusInterface $bus, private DownloadOptionEvaluator $downloadOptionEvaluator, private GetTvShowOptionsHandler $getTvShowOptionsHandler, @@ -36,7 +37,8 @@ readonly class DownloadSeasonHandler implements HandlerInterface public function handle(CommandInterface $command): ResultInterface { - $series = $this->tmdb->mediaDetails($command->imdbId, $command->mediaType); + $series = $this->tmdb->tvshowDetails($command->imdbId); + $this->logger->info('> [DownloadTvSeasonHandler] Executing DownloadTvSeasonHandler for "' . $series->title . '" season ' . $command->season); $episodesInSeason = Map::from($series->episodes[$command->season]); diff --git a/src/Monitor/Action/Handler/MonitorTvEpisodeHandler.php b/src/Monitor/Action/Handler/MonitorTvEpisodeHandler.php index a401192..d1e9ed9 100644 --- a/src/Monitor/Action/Handler/MonitorTvEpisodeHandler.php +++ b/src/Monitor/Action/Handler/MonitorTvEpisodeHandler.php @@ -9,7 +9,7 @@ use App\Download\Framework\Repository\DownloadRepository; use App\Monitor\Action\Command\MonitorMovieCommand; use App\Monitor\Action\Result\MonitorTvEpisodeResult; use App\Monitor\Framework\Repository\MonitorRepository; -use App\Tmdb\Tmdb; +use App\Tmdb\TmdbClient; use App\Torrentio\Action\Command\GetTvShowOptionsCommand; use App\Torrentio\Action\Handler\GetTvShowOptionsHandler; use App\User\Dto\UserPreferencesFactory; @@ -32,7 +32,7 @@ readonly class MonitorTvEpisodeHandler implements HandlerInterface private MessageBusInterface $bus, private LoggerInterface $logger, private MonitorRepository $monitorRepository, - private Tmdb $tmdb, + private TmdbClient $tmdb, private DownloadRepository $downloadRepository, ) {} diff --git a/src/Monitor/Action/Handler/MonitorTvSeasonHandler.php b/src/Monitor/Action/Handler/MonitorTvSeasonHandler.php index f0369e4..35192f3 100644 --- a/src/Monitor/Action/Handler/MonitorTvSeasonHandler.php +++ b/src/Monitor/Action/Handler/MonitorTvSeasonHandler.php @@ -9,7 +9,7 @@ use App\Monitor\Action\Command\MonitorTvSeasonCommand; use App\Monitor\Action\Result\MonitorTvSeasonResult; use App\Monitor\Framework\Entity\Monitor; use App\Monitor\Framework\Repository\MonitorRepository; -use App\Tmdb\Tmdb; +use App\Tmdb\TmdbClient; use DateTimeImmutable; use Doctrine\ORM\EntityManagerInterface; use App\Base\Util\PTN; @@ -26,7 +26,7 @@ readonly class MonitorTvSeasonHandler implements HandlerInterface private EntityManagerInterface $entityManager, private MediaFiles $mediaFiles, private LoggerInterface $logger, - private Tmdb $tmdb, + private TmdbClient $tmdb, private MonitorTvEpisodeHandler $monitorTvEpisodeHandler, ) {} @@ -50,7 +50,7 @@ readonly class MonitorTvSeasonHandler implements HandlerInterface // Compare against list from TMDB $episodesInSeason = Map::from( - $this->tmdb->tvDetails($monitor->getTmdbId())->episodes[$monitor->getSeason()] + $this->tmdb->tvshowDetails($monitor->getImdbId())->episodes[$monitor->getSeason()] )->rekey(fn($episode) => $episode['episode_number']); $this->logger->info('> [MonitorTvSeasonHandler] Found ' . count($episodesInSeason) . ' episodes in season ' . $monitor->getSeason() . ' for title: ' . $monitor->getTitle()); diff --git a/src/Monitor/Action/Handler/MonitorTvShowHandler.php b/src/Monitor/Action/Handler/MonitorTvShowHandler.php index fca1435..3263597 100644 --- a/src/Monitor/Action/Handler/MonitorTvShowHandler.php +++ b/src/Monitor/Action/Handler/MonitorTvShowHandler.php @@ -9,9 +9,8 @@ use App\Monitor\Action\Command\MonitorTvEpisodeCommand; use App\Monitor\Action\Result\MonitorTvShowResult; use App\Monitor\Framework\Entity\Monitor; use App\Monitor\Framework\Repository\MonitorRepository; -use App\Tmdb\Tmdb; +use App\Tmdb\TmdbClient; use Carbon\Carbon; -use Carbon\CarbonImmutable; use DateTimeImmutable; use Doctrine\ORM\EntityManagerInterface; use App\Base\Util\PTN; @@ -29,7 +28,7 @@ readonly class MonitorTvShowHandler implements HandlerInterface private MonitorTvEpisodeHandler $monitorTvEpisodeHandler, private MediaFiles $mediaFiles, private LoggerInterface $logger, - private Tmdb $tmdb, + private TmdbClient $tmdb, ) {} public function handle(CommandInterface $command): ResultInterface @@ -53,7 +52,7 @@ readonly class MonitorTvShowHandler implements HandlerInterface // Compare against list from TMDB $episodesInShow = Map::from( - $this->tmdb->tvDetails($monitor->getTmdbId())->episodes + $this->tmdb->tvshowDetails($monitor->getImdbId())->episodes )->flat(1); $this->logger->info('> [MonitorTvShowHandler] Found ' . count($episodesInShow) . ' episodes for title: ' . $monitor->getTitle()); diff --git a/src/Search/Action/Handler/GetMediaInfoHandler.php b/src/Search/Action/Handler/GetMediaInfoHandler.php index 7f4aa4b..b660158 100644 --- a/src/Search/Action/Handler/GetMediaInfoHandler.php +++ b/src/Search/Action/Handler/GetMediaInfoHandler.php @@ -2,11 +2,9 @@ namespace App\Search\Action\Handler; -use Aimeos\Map; use App\Base\Enum\MediaType; use App\Search\Action\Command\GetMediaInfoCommand; use App\Search\Action\Result\GetMediaInfoResult; -use App\Tmdb\Tmdb; use App\Tmdb\TmdbClient; use App\Tmdb\TmdbResult; use OneToMany\RichBundle\Contract\CommandInterface; diff --git a/src/Search/Action/Handler/SearchHandler.php b/src/Search/Action/Handler/SearchHandler.php index 7752ec6..92c2f1a 100644 --- a/src/Search/Action/Handler/SearchHandler.php +++ b/src/Search/Action/Handler/SearchHandler.php @@ -2,10 +2,8 @@ namespace App\Search\Action\Handler; -use App\Base\Util\ImdbMatcher; use App\Search\Action\Result\RedirectToMediaResult; use App\Search\Action\Result\SearchResult; -use App\Tmdb\Tmdb; use App\Tmdb\TmdbClient; use App\Tmdb\TmdbResult; use OneToMany\RichBundle\Contract\CommandInterface; diff --git a/src/Tmdb/Framework/Controller/ApiController.php b/src/Tmdb/Framework/Controller/ApiController.php index c56bce7..a0c6620 100644 --- a/src/Tmdb/Framework/Controller/ApiController.php +++ b/src/Tmdb/Framework/Controller/ApiController.php @@ -3,7 +3,6 @@ namespace App\Tmdb\Framework\Controller; use App\Base\Util\ImdbMatcher; -use App\Tmdb\Tmdb; use App\Tmdb\TmdbClient; use App\Tmdb\TmdbResult; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; @@ -23,7 +22,7 @@ class ApiController extends AbstractController if (null !== $term) { if (ImdbMatcher::isMatch($term)) { - $tmdbResult = $tmdb->findByImdbId($term); + $tmdbResult = $tmdb->search($term); $results = [ [ 'data' => $tmdbResult, diff --git a/src/Tmdb/HydrationListener.php b/src/Tmdb/HydrationListener.php deleted file mode 100644 index f034a68..0000000 --- a/src/Tmdb/HydrationListener.php +++ /dev/null @@ -1,14 +0,0 @@ -movieRepository = new MovieRepository($this->client); $this->tvRepository = new TvRepository($this->client); $this->tvSeasonRepository = new TvSeasonRepository($this->client); + $this->tvEpisodeRepository = new TvEpisodeRepository($this->client); $this->searchRepository = new SearchRepository($this->client); } @@ -136,6 +134,8 @@ class TmdbClient strtolower($data['name']) !== 'specials'; })->map(function ($data) use ($media) { return $this->tvSeasonDetails($media['id'], $data['season_number'])['episodes']; + })->rekey(function ($data) { + return $data[1]['season_number']; })->toArray(); return $this->parseResult( @@ -152,10 +152,20 @@ class TmdbClient $data['still_path'] = self::POSTER_IMG_PATH . $data['still_path']; $data['poster'] = $data['still_path']; return $data; - })->toArray(); + })->rekey(fn ($data) => $data['episode_number'])->toArray(); return $result; } + public function tvEpisodeDetails(string $tmdbId, int $season, int $episode): ?TmdbResult + { + $result = $this->tvEpisodeRepository->getApi()->getEpisode($tmdbId, $season, $episode, ['append_to_response' => 'external_ids']); + return $this->parseResult( + $result, + MediaType::TvEpisode->value, + $result['external_ids']['imdb_id'] + ); + } + public function relatedMedia(string $tmdbId, string $mediaType, int $resultCount = 6): Map { $repos = [ diff --git a/src/Tmdb/TmdbResultDenormalizer.php b/src/Tmdb/TmdbResultDenormalizer.php index 327bf60..5348c59 100644 --- a/src/Tmdb/TmdbResultDenormalizer.php +++ b/src/Tmdb/TmdbResultDenormalizer.php @@ -28,7 +28,7 @@ class TmdbResultDenormalizer implements DenormalizerInterface MediaType::TvShow->value => 'parseTvShow', 'movie' => 'parseMovie', 'tv' => 'parseTvShow', - 'tvepisodes' => 'parseEpisode', + 'tvepisode' => 'parseEpisode', ]; $denormalized = $this->normalizer->denormalize($data, TmdbResult::class, $format, $context); @@ -79,7 +79,7 @@ class TmdbResultDenormalizer implements DenormalizerInterface } $result->premiereDate = $airDate; - $result->episodeAirDate = $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"; diff --git a/src/Torrentio/Action/Handler/GetMovieOptionsHandler.php b/src/Torrentio/Action/Handler/GetMovieOptionsHandler.php index cc69ef1..8948f2c 100644 --- a/src/Torrentio/Action/Handler/GetMovieOptionsHandler.php +++ b/src/Torrentio/Action/Handler/GetMovieOptionsHandler.php @@ -3,7 +3,7 @@ namespace App\Torrentio\Action\Handler; use App\Base\Service\MediaFiles; -use App\Tmdb\Tmdb; +use App\Tmdb\TmdbClient; use App\Torrentio\Action\Result\GetMovieOptionsResult; use App\Torrentio\Client\Torrentio; use OneToMany\RichBundle\Contract\CommandInterface; @@ -13,14 +13,14 @@ use OneToMany\RichBundle\Contract\ResultInterface; class GetMovieOptionsHandler implements HandlerInterface { public function __construct( - private readonly Tmdb $tmdb, + private readonly TmdbClient $tmdb, private readonly Torrentio $torrentio, private readonly MediaFiles $mediaFiles ) {} public function handle(CommandInterface $command): ResultInterface { - $media = $this->tmdb->mediaDetails($command->imdbId, 'movies'); + $media = $this->tmdb->movieDetails($command->imdbId); return new GetMovieOptionsResult( media: $media, file: $this->mediaFiles->movieExists($media->title), diff --git a/src/Torrentio/Action/Handler/GetTvShowOptionsHandler.php b/src/Torrentio/Action/Handler/GetTvShowOptionsHandler.php index c0117d9..5da3e2d 100644 --- a/src/Torrentio/Action/Handler/GetTvShowOptionsHandler.php +++ b/src/Torrentio/Action/Handler/GetTvShowOptionsHandler.php @@ -2,9 +2,10 @@ namespace App\Torrentio\Action\Handler; +use App\Base\Enum\MediaType; use App\Base\Service\MediaFiles; use App\Library\Dto\MediaFileDto; -use App\Tmdb\Tmdb; +use App\Tmdb\TmdbClient; use App\Torrentio\Action\Command\GetTvShowOptionsCommand; use App\Torrentio\Action\Result\GetTvShowOptionsResult; use App\Torrentio\Client\Torrentio; @@ -16,15 +17,15 @@ use OneToMany\RichBundle\Contract\ResultInterface; class GetTvShowOptionsHandler implements HandlerInterface { public function __construct( - private readonly Tmdb $tmdb, + private readonly TmdbClient $tmdb, private readonly Torrentio $torrentio, private readonly MediaFiles $mediaFiles, ) {} public function handle(CommandInterface $command): ResultInterface { - $media = $this->tmdb->episodeDetails($command->tmdbId, $command->season, $command->episode); - $parentShow = $this->tmdb->mediaDetails($command->imdbId, 'tvshows'); + $media = $this->tmdb->tvEpisodeDetails($command->tmdbId, $command->season, $command->episode); + $parentShow = $this->tmdb->tvshowDetails($command->imdbId); $file = $this->mediaFiles->episodeExists($parentShow->title, $command->season, $command->episode); return new GetTvShowOptionsResult( diff --git a/src/Twig/Components/UpcomingEpisodes.php b/src/Twig/Components/UpcomingEpisodes.php deleted file mode 100644 index 4a8fbf1..0000000 --- a/src/Twig/Components/UpcomingEpisodes.php +++ /dev/null @@ -1,81 +0,0 @@ -getMonitors(); - foreach ($monitors as $monitor) { - $upcomingEpisodes->merge($this->getNextEpisodes($monitor)); - } - - return $upcomingEpisodes->slice(0, $limit)->toArray(); - } - - private function getMonitors() - { - $user = $this->getUser(); - return $user->getMonitors()->filter( - fn (Monitor $monitor) => null === $monitor->getParent() && $monitor->isActive() - ) ?? []; - } - - private function getNextEpisodes(Monitor $monitor): Map - { - $today = CarbonImmutable::now(); - $seriesInfo = $this->tmdb->tvDetails($monitor->getTmdbId()); - - switch ($monitor->getMonitorType()) { - case "tvseason": - $episodes = Map::from($seriesInfo->episodes[$monitor->getSeason()]) - ->filter(function (array $episode) use ($today) { - $airDate = CarbonImmutable::parse($episode['air_date']); - return $airDate->lte($today); - }) - ; - break; - case "tvshows": - $episodes = []; - foreach ($seriesInfo->episodes as $season => $episodeList) { - $episodes = array_merge($episodes, $episodeList); - } - $episodes = Map::from($episodes) - ->filter(function (array $episode) use ($today) { - $airDate = CarbonImmutable::parse($episode['air_date']); - return $airDate->gte($today); - }) - ; - break; - } - - return $episodes->map(function (array $episode) use ($monitor) { - return new UpcomingEpisodeDto( - $monitor->getTitle(), - $episode['air_date'], - $episode['name'], - $episode['episode_number'], - ); - }); - } -} diff --git a/tests/Monitor/MonitorTvShowHandlerTest.php b/tests/Monitor/MonitorTvShowHandlerTest.php index 1ef6300..4620338 100644 --- a/tests/Monitor/MonitorTvShowHandlerTest.php +++ b/tests/Monitor/MonitorTvShowHandlerTest.php @@ -9,7 +9,7 @@ use App\Monitor\Action\Handler\MonitorTvShowHandler; use App\Monitor\Action\Result\MonitorTvShowResult; use App\Monitor\Framework\Entity\Monitor; use App\Monitor\Framework\Repository\MonitorRepository; -use App\Tmdb\Tmdb; +use App\Tmdb\TmdbClient; use Doctrine\ORM\EntityManagerInterface; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; @@ -22,7 +22,7 @@ class MonitorTvShowHandlerTest extends TestCase private MonitorTvEpisodeHandler $episodeHandler; private MediaFiles $mediaFiles; private LoggerInterface $logger; - private Tmdb $tmdb; + private TmdbClient $tmdb; protected function setUp(): void { @@ -31,7 +31,7 @@ class MonitorTvShowHandlerTest extends TestCase $this->episodeHandler = $this->createMock(MonitorTvEpisodeHandler::class); $this->mediaFiles = $this->createMock(MediaFiles::class); $this->logger = $this->createMock(LoggerInterface::class); - $this->tmdb = $this->createMock(Tmdb::class); + $this->tmdb = $this->createMock(TmdbClient::class); $this->handler = new MonitorTvShowHandler( $this->monitorRepository,