Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8cd004db4a | |||
| 6cc8985c4d | |||
| 9bfd92a011 | |||
| ac6276f444 | |||
| 6d4bbf2e72 | |||
| 8004de1dc8 | |||
| 2d9a2a1f14 | |||
| b6bc1645b4 |
@@ -3,8 +3,6 @@
|
||||
|
||||
frankenphp {
|
||||
{$FRANKENPHP_CONFIG}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,11 +33,12 @@ final class SearchController extends AbstractController
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/result/{mediaType}/{tmdbId}', name: 'app_search_result')]
|
||||
#[Route('/result/{mediaType}/{imdbId}', name: 'app_search_result')]
|
||||
public function result(
|
||||
GetMediaInfoInput $input,
|
||||
): Response {
|
||||
$result = $this->getMediaInfoHandler->handle($input->toCommand());
|
||||
|
||||
$this->warmDownloadOptionCache($result->media);
|
||||
|
||||
return $this->render('search/result.html.twig', [
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
namespace App\Download\Framework\Entity;
|
||||
|
||||
use App\Download\Framework\Repository\DownloadRepository;
|
||||
use App\Torrentio\Result\ResultFactory;
|
||||
use App\Torrentio\Result\TorrentioResult;
|
||||
use App\User\Framework\Entity\User;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Nihilarr\PTN;
|
||||
use Symfony\UX\Turbo\Attribute\Broadcast;
|
||||
|
||||
#[ORM\Entity(repositoryClass: DownloadRepository::class)]
|
||||
@@ -162,4 +165,16 @@ class Download
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPtn(): object
|
||||
{
|
||||
$ptn = (object) (new PTN())->parse($this->filename);
|
||||
|
||||
if ($this->mediaType === "tvshows") {
|
||||
$ptn->season = str_pad($ptn->season, 2, "0", STR_PAD_LEFT);
|
||||
$ptn->episode = str_pad($ptn->episode, 2, "0", STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
return $ptn;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,27 +25,31 @@ class DownloadRepository extends ServiceEntityRepository
|
||||
$this->paginator = $paginator;
|
||||
}
|
||||
|
||||
public function getCompletePaginated(UserInterface $user, int $pageNumber = 1, int $perPage = 10): Paginator
|
||||
public function getCompletePaginated(UserInterface $user, int $pageNumber = 1, int $perPage = 10, string $term = ""): Paginator
|
||||
{
|
||||
$query = $this->createQueryBuilder('d')
|
||||
->andWhere('d.status IN (:statuses)')
|
||||
->andWhere('d.user = :user')
|
||||
->andWhere('(d.title LIKE :term OR d.imdbId LIKE :term)')
|
||||
->orderBy('d.id', 'DESC')
|
||||
->setParameter('statuses', ['Complete'])
|
||||
->setParameter('user', $user)
|
||||
->setParameter('term', '%' . $term . '%')
|
||||
->getQuery();
|
||||
|
||||
return $this->paginator->paginate($query, $pageNumber, $perPage);
|
||||
}
|
||||
|
||||
public function getActivePaginated(UserInterface $user, int $pageNumber = 1, int $perPage = 5): Paginator
|
||||
public function getActivePaginated(UserInterface $user, int $pageNumber = 1, int $perPage = 5, string $term = ""): Paginator
|
||||
{
|
||||
$query = $this->createQueryBuilder('d')
|
||||
->andWhere('d.status IN (:statuses)')
|
||||
->andWhere('d.user = :user')
|
||||
->andWhere('(d.title LIKE :term OR d.imdbId LIKE :term)')
|
||||
->orderBy('d.id', 'ASC')
|
||||
->setParameter('statuses', ['New', 'In Progress'])
|
||||
->setParameter('user', $user)
|
||||
->setParameter('term', '%' . $term . '%')
|
||||
->getQuery();
|
||||
|
||||
return $this->paginator->paginate($query, $pageNumber, $perPage);
|
||||
|
||||
@@ -8,7 +8,7 @@ use OneToMany\RichBundle\Contract\CommandInterface;
|
||||
class GetMediaInfoCommand implements CommandInterface
|
||||
{
|
||||
public function __construct(
|
||||
public string $tmdbId,
|
||||
public string $imdbId,
|
||||
public string $mediaType,
|
||||
) {}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ class GetMediaInfoHandler implements HandlerInterface
|
||||
|
||||
public function handle(CommandInterface $command): ResultInterface
|
||||
{
|
||||
$media = $this->tmdb->mediaDetails($command->tmdbId, $command->mediaType);
|
||||
$media = $this->tmdb->mediaDetails($command->imdbId, $command->mediaType);
|
||||
|
||||
return new GetMediaInfoResult($media);
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ use OneToMany\RichBundle\Contract\InputInterface;
|
||||
class GetMediaInfoInput implements InputInterface
|
||||
{
|
||||
public function __construct(
|
||||
#[SourceRoute('tmdbId')]
|
||||
public string $tmdbId,
|
||||
#[SourceRoute('imdbId')]
|
||||
public string $imdbId,
|
||||
|
||||
#[SourceRoute('mediaType')]
|
||||
public string $mediaType,
|
||||
@@ -21,6 +21,6 @@ class GetMediaInfoInput implements InputInterface
|
||||
|
||||
public function toCommand(): CommandInterface
|
||||
{
|
||||
return new GetMediaInfoCommand($this->tmdbId, $this->mediaType);
|
||||
return new GetMediaInfoCommand($this->imdbId, $this->mediaType);
|
||||
}
|
||||
}
|
||||
@@ -133,9 +133,12 @@ class Tmdb
|
||||
if (!$result instanceof Movie && !$result instanceof Tv) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$results[] = $this->parseResult($result);
|
||||
}
|
||||
|
||||
$results = array_filter($results, fn ($result) => null !== $result->imdbId);
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
@@ -143,7 +146,16 @@ class Tmdb
|
||||
{
|
||||
$finder = new Find($this->client);
|
||||
$result = $finder->findBy($id, ['external_source' => 'imdb_id']);
|
||||
return $this->parseResult($result);
|
||||
|
||||
if (count($result['movie_results']) > 0) {
|
||||
return $result['movie_results'][0]['id'];
|
||||
} elseif (count($result['tv_results']) > 0) {
|
||||
return $result['tv_results'][0]['id'];
|
||||
} elseif (count($result['tv_episode_results']) > 0) {
|
||||
return $result['tv_episode_results'][0]['show_id'];
|
||||
}
|
||||
|
||||
throw new \Exception("No results found for $id");
|
||||
}
|
||||
|
||||
public function movieDetails(string $id)
|
||||
@@ -183,6 +195,8 @@ class Tmdb
|
||||
|
||||
public function mediaDetails(string $id, string $type)
|
||||
{
|
||||
$id = $this->find($id);
|
||||
|
||||
if ($type === "movies") {
|
||||
return $this->movieDetails($id);
|
||||
} else {
|
||||
|
||||
@@ -19,7 +19,7 @@ class GetMovieOptionsHandler implements HandlerInterface
|
||||
public function handle(CommandInterface $command): ResultInterface
|
||||
{
|
||||
return new GetMovieOptionsResult(
|
||||
media: $this->tmdb->mediaDetails($command->tmdbId, 'movies'),
|
||||
media: $this->tmdb->mediaDetails($command->imdbId, 'movies'),
|
||||
results: $this->torrentio->search($command->imdbId, 'movies'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ final class DownloadList extends AbstractController
|
||||
|
||||
use PaginateTrait;
|
||||
|
||||
#[LiveProp(writable: true)]
|
||||
public string $term = "";
|
||||
|
||||
#[LiveProp(writable: true)]
|
||||
public string $type;
|
||||
|
||||
@@ -31,9 +34,9 @@ final class DownloadList extends AbstractController
|
||||
public function getDownloads()
|
||||
{
|
||||
if ($this->type === "active") {
|
||||
return $this->downloadRepository->getActivePaginated($this->getUser(), $this->pageNumber, $this->perPage);
|
||||
return $this->downloadRepository->getActivePaginated($this->getUser(), $this->pageNumber, $this->perPage, $this->term);
|
||||
} elseif ($this->type === "complete") {
|
||||
return $this->downloadRepository->getCompletePaginated($this->getUser(), $this->pageNumber, $this->perPage);
|
||||
return $this->downloadRepository->getCompletePaginated($this->getUser(), $this->pageNumber, $this->perPage, $this->term);
|
||||
}
|
||||
|
||||
return [];
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
<div{{ attributes.defaults(stimulus_controller('download_list')) }} class="min-w-48" >
|
||||
{% set table_body_id = (type == "complete") ? "complete_downloads" : "active_downloads" %}
|
||||
|
||||
{% if this.isWidget == false %}
|
||||
<div class="flex flex-row mb-2">
|
||||
<twig:DownloadSearch search_path="app_search" placeholder="Find one of your downloads..." />
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<table id="downloads" class="divide-y divide-gray-200 bg-gray-50 overflow-hidden rounded-lg table-auto w-full" {{ turbo_stream_listen('App\\Download\\Framework\\Entity\\Download') }}>
|
||||
<thead>
|
||||
<tr class="bg-orange-500 bg-filter bg-blur-lg bg-opacity-80 text-gray-950">
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
<tr{{ attributes }} id="ad_download_{{ download.id }}">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-stone-800 truncate">
|
||||
{{ download.title }}
|
||||
<a href="{{ path('app_search_result', {imdbId: download.imdbId, mediaType: download.mediaType}) }}"
|
||||
class="hover:underline mr-1"
|
||||
>
|
||||
{{ download.title }}
|
||||
</a>
|
||||
|
||||
{% if download.mediaType == "tvshows" %}
|
||||
— <span class="ml-1">(S{{ download.ptn.season }}E{{ download.ptn.episode }})</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
{% if isWidget == false %}
|
||||
@@ -14,11 +22,11 @@
|
||||
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm align-middle text-gray-800 dark:text-gray-50">
|
||||
{% if download.progress < 100 %}
|
||||
<div id="download_progress_{{ download.id }}" class="border-2 border-green-700 rounded-md w-full h-6 align-middle overflow-hidden">
|
||||
<div class="text-green-700 rounded-sm text-bold text-gray-950 text-center bg-green-600 h-5"
|
||||
style="width:56%">
|
||||
<span>{{ download.progress }}%</span>
|
||||
<div id="download_progress_{{ download.id }}" class="border-2 border-green-600 rounded-md text-center w-full h-6 align-middle overflow-hidden">
|
||||
<div class="text-black text-center rounded-sm text-bold bg-green-300 h-5 relative z-10"
|
||||
style="width:{{ download.progress }}%">
|
||||
</div>
|
||||
<div class="absolute text-black text-center" style="z-index: 400;margin-top: -1.25rem; margin-left: 1.2rem">{{ download.progress }}%</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<twig:StatusBadge color="green" status="Complete" />
|
||||
|
||||
29
templates/components/DownloadSearch.html.twig
Normal file
29
templates/components/DownloadSearch.html.twig
Normal file
@@ -0,0 +1,29 @@
|
||||
<div {{ attributes }} class="w-full max-w-sm min-w-[200px]">
|
||||
<div class="relative">
|
||||
<form>
|
||||
<input
|
||||
data-model="term"
|
||||
class="w-full bg-orange-500 rounded-md bg-clip-padding backdrop-filter
|
||||
backdrop-blur-md bg-opacity-40 placeholder:text-slate-200 text-gray-50
|
||||
text-sm border border-orange-500 rounded-md pl-3 pr-28 py-2 transition
|
||||
duration-300 ease focus:outline-none focus:border-orange-400 hover:border-orange-300
|
||||
shadow-sm focus:shadow"
|
||||
placeholder="{{ placeholder ?? 'TV Show, Movie...' }}"
|
||||
/>
|
||||
<button
|
||||
class="absolute top-1 right-1 flex items-center rounded
|
||||
bg-green-600 py-1 px-2.5 border border-transparent text-center
|
||||
text-sm text-white transition-all
|
||||
focus:bg-green-700 active:bg-green-700 hover:bg-green-700
|
||||
|
||||
text-white bg-green-600 text-sm
|
||||
border border-green-500
|
||||
backdrop-filter backdrop-blur-md bg-opacity-80
|
||||
"
|
||||
type="submit"
|
||||
>
|
||||
Search
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,13 +1,13 @@
|
||||
<div{{ attributes }}>
|
||||
<a href="{{ path('app_search_result', {
|
||||
mediaType: mediaType,
|
||||
tmdbId: tmdbId
|
||||
imdbId: imdbId
|
||||
}) }}">
|
||||
<img src="{{ image }}" class="w-40 rounded-md" />
|
||||
</a>
|
||||
<a href="{{ path('app_search_result', {
|
||||
mediaType: mediaType,
|
||||
tmdbId: tmdbId
|
||||
imdbId: imdbId
|
||||
}) }}">
|
||||
<h3 class="text-center text-gray-50 max-w-[16ch] text-extrabold">{{ title }}</h3>
|
||||
</a>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
</p>
|
||||
</div>
|
||||
<a class="h-9 rounded-md py-1 px-2 bg-green-600 text-gray-50"
|
||||
href="{{ path('app_search_result', {mediaType: mediaType, tmdbId: tmdbId}) }}"
|
||||
href="{{ path('app_search_result', {mediaType: mediaType, imdbId: imdbId}) }}"
|
||||
>choose</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<div class="flex flex-col gap-4">
|
||||
<twig:Card title="Popular Movies" contentClass="flex flex-row justify-between w-full">
|
||||
{% for movie in popular_movies %}
|
||||
<twig:Poster imdbId=""
|
||||
<twig:Poster imdbId="{{ movie.imdbId }}"
|
||||
tmdbId="{{ movie.tmdbId }}"
|
||||
title="{{ movie.title }}"
|
||||
description="{{ movie.description }}"
|
||||
@@ -34,7 +34,7 @@
|
||||
</twig:Card>
|
||||
<twig:Card title="Popular TV Shows" contentClass="flex flex-row justify-between w-full">
|
||||
{% for movie in popular_tvshows %}
|
||||
<twig:Poster imdbId=""
|
||||
<twig:Poster imdbId="{{ movie.imdbId }}"
|
||||
tmdbId="{{ movie.tmdbId }}"
|
||||
title="{{ movie.title }}"
|
||||
description="{{ movie.description }}"
|
||||
|
||||
Reference in New Issue
Block a user