feat: tv episode results

This commit is contained in:
2025-04-21 22:39:22 -05:00
parent 7f29d737c1
commit d60fae24d1
9 changed files with 253 additions and 7 deletions

View File

@@ -3,7 +3,9 @@
namespace App\Controller;
use App\Torrentio\Action\Handler\GetMovieOptionsHandler;
use App\Torrentio\Action\Handler\GetTvShowOptionsHandler;
use App\Torrentio\Action\Input\GetMovieOptionsInput;
use App\Torrentio\Action\Input\GetTvShowOptionsInput;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
@@ -12,9 +14,10 @@ final class TorrentioController extends AbstractController
{
public function __construct(
private readonly GetMovieOptionsHandler $getMovieOptionsHandler,
private readonly GetTvShowOptionsHandler $getTvShowOptionsHandler,
) {}
#[Route('/torrentio/movies/{imdbId}', name: 'app_torrentio')]
#[Route('/torrentio/movies/{imdbId}', name: 'app_torrentio_movies')]
public function movieOptions(GetMovieOptionsInput $input): Response
{
$results = $this->getMovieOptionsHandler->handle($input->toCommand());
@@ -23,4 +26,14 @@ final class TorrentioController extends AbstractController
'results' => $results,
]);
}
#[Route('/torrentio/tvshows/{tmdbId}/{imdbId}/{season?}/{episode?}', name: 'app_torrentio_tvshows')]
public function tvShowOptions(GetTvShowOptionsInput $input): Response
{
$results = $this->getTvShowOptionsHandler->handle($input->toCommand());
return $this->render('torrentio/tvshows.html.twig', [
'results' => $results,
]);
}
}

View File

@@ -27,6 +27,7 @@ use Tmdb\Model\Search\SearchQuery\MovieSearchQuery;
use Tmdb\Model\Tv;
use Tmdb\Repository\MovieRepository;
use Tmdb\Repository\SearchRepository;
use Tmdb\Repository\TvEpisodeRepository;
use Tmdb\Repository\TvRepository;
use Tmdb\Repository\TvSeasonRepository;
use Tmdb\Token\Api\ApiToken;
@@ -139,7 +140,14 @@ class Tmdb
$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");
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)
@@ -192,6 +200,19 @@ class Tmdb
);
}
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'],
@@ -204,8 +225,13 @@ class Tmdb
);
}
$posterBasePath = self::POSTER_IMG_PATH;
$result = ("movie" === $mediaType) ? parseMovie($data, $posterBasePath) : parseTvShow($data, $posterBasePath);
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;
}

View File

@@ -0,0 +1,15 @@
<?php
namespace App\Torrentio\Action\Command;
use OneToMany\RichBundle\Contract\CommandInterface;
class GetTvShowOptionsCommand implements CommandInterface
{
public function __construct(
public string $tmdbId,
public string $imdbId,
public ?string $season,
public ?string $episode,
) {}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Torrentio\Action\Handler;
use App\Tmdb\Tmdb;
use App\Torrentio\Action\Result\GetTvShowOptionsResult;
use App\Torrentio\Client\Torrentio;
use OneToMany\RichBundle\Contract\CommandInterface;
use OneToMany\RichBundle\Contract\HandlerInterface;
use OneToMany\RichBundle\Contract\ResultInterface;
class GetTvShowOptionsHandler implements HandlerInterface
{
public function __construct(
private readonly Tmdb $tmdb,
private readonly Torrentio $torrentio,
) {}
public function handle(CommandInterface $command): ResultInterface
{
return new GetTvShowOptionsResult(
media: $this->tmdb->episodeDetails($command->tmdbId, $command->season, $command->episode),
season: $command->season,
episode: $command->episode,
results: $this->torrentio->fetchEpisodeResults(
$command->imdbId,
$command->season,
$command->episode,
)
);
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Torrentio\Action\Input;
use App\Torrentio\Action\Command\GetTvShowOptionsCommand;
use OneToMany\RichBundle\Attribute\SourceRoute;
use OneToMany\RichBundle\Contract\CommandInterface;
use OneToMany\RichBundle\Contract\InputInterface;
class GetTvShowOptionsInput implements InputInterface
{
public function __construct(
#[SourceRoute('tmdbId')]
public string $tmdbId,
#[SourceRoute('imdbId')]
public string $imdbId,
#[SourceRoute('season', nullify: true)]
public ?string $season,
#[SourceRoute('episode', nullify: true)]
public string $episode,
) {}
public function toCommand(): CommandInterface
{
return new GetTvShowOptionsCommand(
$this->tmdbId,
$this->imdbId,
$this->season,
$this->episode
);
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace App\Torrentio\Action\Result;
use App\Tmdb\TmdbResult;
use OneToMany\RichBundle\Contract\ResultInterface;
class GetTvShowOptionsResult implements ResultInterface
{
public function __construct(
public TmdbResult $media,
public string $season,
public string $episode,
public array $results
) {}
}