fix: renders media exists badge on movie results
This commit is contained in:
@@ -20,8 +20,10 @@ export default class extends Controller {
|
|||||||
|
|
||||||
options = []
|
options = []
|
||||||
optionsLoaded = false
|
optionsLoaded = false
|
||||||
|
resultCountEl = null
|
||||||
|
|
||||||
async connect() {
|
async connect() {
|
||||||
|
this.resultCountEl = document.querySelector('#movie_results_count');
|
||||||
await this.setOptions();
|
await this.setOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,6 +38,7 @@ export default class extends Controller {
|
|||||||
this.options.forEach((option) => option.querySelector('.download-btn').dataset['title'] = this.titleValue);
|
this.options.forEach((option) => option.querySelector('.download-btn').dataset['title'] = this.titleValue);
|
||||||
this.dispatch('optionsLoaded', {detail: {options: this.options}})
|
this.dispatch('optionsLoaded', {detail: {options: this.options}})
|
||||||
this.loadingIconOutlet.toggleIcon();
|
this.loadingIconOutlet.toggleIcon();
|
||||||
|
this.resultCountEl.innerText = this.options.length;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,5 +105,6 @@ export default class extends Controller {
|
|||||||
count = count + 1;
|
count = count + 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.resultCountEl.innerText = count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ namespace App\Library\Action\Handler;
|
|||||||
use App\Base\Service\MediaFiles;
|
use App\Base\Service\MediaFiles;
|
||||||
use App\Library\Action\Command\LibrarySearchCommand;
|
use App\Library\Action\Command\LibrarySearchCommand;
|
||||||
use App\Library\Action\Result\LibrarySearchResult;
|
use App\Library\Action\Result\LibrarySearchResult;
|
||||||
|
use App\Library\Dto\MediaFileDto;
|
||||||
|
use Nihilarr\PTN;
|
||||||
use OneToMany\RichBundle\Contract\CommandInterface;
|
use OneToMany\RichBundle\Contract\CommandInterface;
|
||||||
use OneToMany\RichBundle\Contract\HandlerInterface;
|
use OneToMany\RichBundle\Contract\HandlerInterface;
|
||||||
use OneToMany\RichBundle\Contract\ResultInterface;
|
use OneToMany\RichBundle\Contract\ResultInterface;
|
||||||
@@ -16,6 +18,7 @@ class LibrarySearchHandler implements HandlerInterface
|
|||||||
{
|
{
|
||||||
private array $searchTypes = [
|
private array $searchTypes = [
|
||||||
'episode_by_title' => 'episodeByTitle',
|
'episode_by_title' => 'episodeByTitle',
|
||||||
|
'movie_by_title' => 'movieByTitle',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
@@ -31,6 +34,14 @@ class LibrarySearchHandler implements HandlerInterface
|
|||||||
|
|
||||||
private function getSearchType(CommandInterface $command): ?string
|
private function getSearchType(CommandInterface $command): ?string
|
||||||
{
|
{
|
||||||
|
if (!is_null($command->title) &&
|
||||||
|
is_null($command->imdbId) &&
|
||||||
|
is_null($command->season) &&
|
||||||
|
is_null($command->episode)
|
||||||
|
) {
|
||||||
|
return 'movie_by_title';
|
||||||
|
}
|
||||||
|
|
||||||
if ((!is_null($command->title) || is_null($command->imdbId)) &&
|
if ((!is_null($command->title) || is_null($command->imdbId)) &&
|
||||||
!is_null($command->season) &&
|
!is_null($command->season) &&
|
||||||
!is_null($command->episode)
|
!is_null($command->episode)
|
||||||
@@ -47,14 +58,26 @@ class LibrarySearchHandler implements HandlerInterface
|
|||||||
(int) $command->season,
|
(int) $command->season,
|
||||||
(int) $command->episode,
|
(int) $command->episode,
|
||||||
);
|
);
|
||||||
|
|
||||||
$exists = $result instanceof \SplFileInfo;
|
$exists = $result instanceof \SplFileInfo;
|
||||||
|
|
||||||
|
|
||||||
return new LibrarySearchResult(
|
return new LibrarySearchResult(
|
||||||
input: $command,
|
input: $command,
|
||||||
exists: $exists,
|
exists: $exists,
|
||||||
file: true === $exists ? $result->getFileInfo() : null,
|
file: true === $exists ? MediaFileDto::fromSplFileInfo($result) : null,
|
||||||
|
ptn: true === $exists ? (object) new PTN()->parse($result->getFilename()) : null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function movieByTitle(CommandInterface $command): ?LibrarySearchResult
|
||||||
|
{
|
||||||
|
$result = $this->mediaFiles->movieExists($command->title);
|
||||||
|
$exists = $result instanceof \SplFileInfo;
|
||||||
|
|
||||||
|
return new LibrarySearchResult(
|
||||||
|
input: $command,
|
||||||
|
exists: $exists,
|
||||||
|
file: true === $exists ? MediaFileDto::fromSplFileInfo($result) : null,
|
||||||
|
ptn: true === $exists ? (object) new PTN()->parse($result->getFilename()) : null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Library\Action\Result;
|
namespace App\Library\Action\Result;
|
||||||
|
|
||||||
|
use App\Library\Dto\MediaFileDto;
|
||||||
use OneToMany\RichBundle\Contract\ResultInterface;
|
use OneToMany\RichBundle\Contract\ResultInterface;
|
||||||
|
|
||||||
class LibrarySearchResult implements ResultInterface
|
class LibrarySearchResult implements ResultInterface
|
||||||
@@ -9,7 +10,7 @@ class LibrarySearchResult implements ResultInterface
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
public object|array $input,
|
public object|array $input,
|
||||||
public bool $exists,
|
public bool $exists,
|
||||||
public ?\SplFileInfo $file = null,
|
public ?MediaFileDto $file = null,
|
||||||
public ?object $ptn = null,
|
public ?object $ptn = null,
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|||||||
23
src/Library/Dto/MediaFileDto.php
Normal file
23
src/Library/Dto/MediaFileDto.php
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Library\Dto;
|
||||||
|
|
||||||
|
readonly class MediaFileDto
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public string $path,
|
||||||
|
public string $filename,
|
||||||
|
public string $extension,
|
||||||
|
public string $size,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public static function fromSplFileInfo(\SplFileInfo $fileInfo): self
|
||||||
|
{
|
||||||
|
return new static(
|
||||||
|
path: $fileInfo->getRealPath(),
|
||||||
|
filename: $fileInfo->getFilename(),
|
||||||
|
extension: $fileInfo->getExtension(),
|
||||||
|
size: $fileInfo->getSize(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,7 +37,8 @@ final class WebController extends AbstractController
|
|||||||
public function result(
|
public function result(
|
||||||
GetMediaInfoInput $input,
|
GetMediaInfoInput $input,
|
||||||
?int $season = null,
|
?int $season = null,
|
||||||
): Response {
|
): Response
|
||||||
|
{
|
||||||
$result = $this->getMediaInfoHandler->handle($input->toCommand());
|
$result = $this->getMediaInfoHandler->handle($input->toCommand());
|
||||||
|
|
||||||
return $this->render('search/result.html.twig', [
|
return $this->render('search/result.html.twig', [
|
||||||
@@ -52,32 +53,4 @@ final class WebController extends AbstractController
|
|||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function warmDownloadOptionCache(TmdbResult $result)
|
|
||||||
{
|
|
||||||
if ($result->mediaType === 'tvshows') {
|
|
||||||
// dispatches a job to get the download options
|
|
||||||
// for each episode and load them in cache
|
|
||||||
foreach ($result->episodes as $season => $episodes) {
|
|
||||||
// Only do the first 2 seasons, so we reduce
|
|
||||||
// getting rate-limited by Torrentio
|
|
||||||
if ($season > 2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
foreach ($episodes as $episode) {
|
|
||||||
$this->bus->dispatch(new GetTvShowOptionsCommand(
|
|
||||||
tmdbId: $result->tmdbId,
|
|
||||||
imdbId: $result->imdbId,
|
|
||||||
season: $season,
|
|
||||||
episode: $episode['episode_number'],
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} elseif ($result->mediaType === 'movies') {
|
|
||||||
$this->bus->dispatch(new GetMovieOptionsCommand(
|
|
||||||
$result->tmdbId,
|
|
||||||
$result->imdbId,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -301,6 +301,7 @@ class Tmdb
|
|||||||
description: $data['overview'],
|
description: $data['overview'],
|
||||||
year: (new \DateTime($data['release_date']))->format('Y'),
|
year: (new \DateTime($data['release_date']))->format('Y'),
|
||||||
mediaType: "movies",
|
mediaType: "movies",
|
||||||
|
episodeAirDate: (new \DateTime($data['release_date']))->format('m/d/Y'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<template data-popover-target="content">
|
<template data-popover-target="content">
|
||||||
<div data-popover-target="card"
|
<div data-popover-target="card"
|
||||||
class="absolute z-40 p-1 bg-stone-400 p-1 text-black rounded-md m-1 animate-fade">
|
class="absolute z-40 p-1 bg-stone-400 p-1 text-black rounded-md m-1 animate-fade">
|
||||||
<p class="font-bold text-sm text-left">Existing file(s) for this episode:</p>
|
<p class="font-bold text-sm text-left">Existing file(s) for this media:</p>
|
||||||
<ul class="list-disc ml-3">
|
<ul class="list-disc ml-3">
|
||||||
<li class="font-normal">{{ result.file.filename|strip_media_path }} — <strong>{{ result.file.size|filesize }}</strong></li>
|
<li class="font-normal">{{ result.file.filename|strip_media_path }} — <strong>{{ result.file.size|filesize }}</strong></li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
{% if result.exists == false %}
|
{% if result.exists == false %}
|
||||||
<small class="py-1 px-1.5 mr-1 grow-0 font-bold bg-rose-600 rounded-lg text-white"
|
<small class="py-1 px-1.5 mr-1 grow-0 font-bold bg-rose-600 rounded-lg text-white"
|
||||||
title="Episode has not been downloaded yet.">
|
title="Media has not been downloaded yet.">
|
||||||
missing
|
missing
|
||||||
</small>
|
</small>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -49,9 +49,32 @@
|
|||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="text-gray-50">
|
<p class="text-gray-50">
|
||||||
{{ results.media.description }}
|
{{ results.media.description }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
{% if "movies" == results.media.mediaType %}
|
||||||
|
<div class="flex flex-row justify-start items-end grow">
|
||||||
|
<span class="py-1 px-1.5 mr-1 grow-0 font-bold text-xs bg-green-600 rounded-lg hover:cursor-pointer hover:bg-green-700 text-white">
|
||||||
|
<span id="movie_results_count">-</span> results
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<small class="py-1 px-1.5 mr-1 grow-0 font-bold bg-gray-700 rounded-lg font-normal text-white" title="Release date {{ results.media.episodeAirDate }}">
|
||||||
|
{{ results.media.episodeAirDate|date(null, 'UTC') }}
|
||||||
|
</small>
|
||||||
|
|
||||||
|
<twig:Turbo:Frame id="meb_{{ results.media.imdbId }}" src="{{ path('api.library.search', {
|
||||||
|
title: results.media.title,
|
||||||
|
block: 'media_exists_badge',
|
||||||
|
target: "meb_" ~ results.media.imdbId
|
||||||
|
}) }}">
|
||||||
|
<small class="py-1 px-1.5 mr-1 grow-0 font-bold bg-rose-600 rounded-lg text-white" title="Movie has not been downloaded yet.">
|
||||||
|
missing
|
||||||
|
</small>
|
||||||
|
</twig:Turbo:Frame>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
{% extends 'base.html.twig' %}
|
|
||||||
|
|
||||||
{% block title %}Hello TorrentioController!{% endblock %}
|
|
||||||
|
|
||||||
{% block body %}
|
|
||||||
<style>
|
|
||||||
.example-wrapper { margin: 1em auto; max-width: 800px; width: 95%; font: 18px/1.5 sans-serif; }
|
|
||||||
.example-wrapper code { background: #F5F5F5; padding: 2px 6px; }
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="example-wrapper">
|
|
||||||
<h1>Hello {{ controller_name }}! ✅</h1>
|
|
||||||
|
|
||||||
This friendly message is coming from:
|
|
||||||
<ul>
|
|
||||||
<li>Your controller at <code>/var/www/src/Controller/TorrentioController.php</code></li>
|
|
||||||
<li>Your template at <code>/var/www/templates/torrentio/index.html.twig</code></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,13 +1,4 @@
|
|||||||
<div class="p-4 flex flex-col gap-6 bg-orange-500 bg-clip-padding backdrop-filter backdrop-blur-md bg-opacity-60 rounded-md">
|
<div class="p-4 flex flex-col gap-6 bg-orange-500 bg-clip-padding backdrop-filter backdrop-blur-md bg-opacity-60 rounded-md">
|
||||||
{% if results.file != false %}
|
|
||||||
<div class="p-3 bg-stone-400 p-1 text-black rounded-md m-1 animate-fade">
|
|
||||||
<p class="font-bold text-sm text-left">Existing file(s) for this movie:</p>
|
|
||||||
<ul class="list-disc ml-3 overflow-scroll">
|
|
||||||
<li class="font-normal">{{ results.file.realPath|strip_media_path }} — <strong>{{ results.file.size|filesize }}</strong></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div class="overflow-hidden rounded-md">
|
<div class="overflow-hidden rounded-md">
|
||||||
{{ include('torrentio/partial/option-table.html.twig', {controller: 'movie-results'}) }}
|
{{ include('torrentio/partial/option-table.html.twig', {controller: 'movie-results'}) }}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user