feat: movie monitoring
This commit is contained in:
1
.env.dev
1
.env.dev
@@ -5,3 +5,4 @@ DOWNLOAD_DIR=./movies
|
|||||||
MERCURE_URL=http://mercure/.well-known/mercure
|
MERCURE_URL=http://mercure/.well-known/mercure
|
||||||
MERCURE_PUBLIC_URL=https://dev.caldwell.digital/hub/.well-known/mercure
|
MERCURE_PUBLIC_URL=https://dev.caldwell.digital/hub/.well-known/mercure
|
||||||
MERCURE_JWT_SECRET="!ChangeThisMercureHubJWTSecretKey!"
|
MERCURE_JWT_SECRET="!ChangeThisMercureHubJWTSecretKey!"
|
||||||
|
MONITOR_FREQUENCY="* * * * *"
|
||||||
|
|||||||
@@ -31,6 +31,13 @@ services:
|
|||||||
- ./var/download:/var/download
|
- ./var/download:/var/download
|
||||||
command: php ./bin/console messenger:consume async -v --time-limit=3600 --limit=10
|
command: php ./bin/console messenger:consume async -v --time-limit=3600 --limit=10
|
||||||
|
|
||||||
|
scheduler:
|
||||||
|
build: .
|
||||||
|
volumes:
|
||||||
|
- ./:/var/www
|
||||||
|
- ./var/download:/var/download
|
||||||
|
command: php ./bin/console messenger:consume scheduler_movie_monitor -v --time-limit=3600
|
||||||
|
|
||||||
mercure:
|
mercure:
|
||||||
image: dunglas/mercure
|
image: dunglas/mercure
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|||||||
@@ -9,11 +9,11 @@
|
|||||||
"ext-iconv": "*",
|
"ext-iconv": "*",
|
||||||
"1tomany/rich-bundle": "^1.8",
|
"1tomany/rich-bundle": "^1.8",
|
||||||
"aimeos/map": "^3.12",
|
"aimeos/map": "^3.12",
|
||||||
"carbondate/carbon": "*",
|
|
||||||
"doctrine/dbal": "^3",
|
"doctrine/dbal": "^3",
|
||||||
"doctrine/doctrine-bundle": "^2.14",
|
"doctrine/doctrine-bundle": "^2.14",
|
||||||
"doctrine/doctrine-migrations-bundle": "^3.4",
|
"doctrine/doctrine-migrations-bundle": "^3.4",
|
||||||
"doctrine/orm": "^3.3",
|
"doctrine/orm": "^3.3",
|
||||||
|
"dragonmantank/cron-expression": "^3.4",
|
||||||
"nihilarr/parse-torrent-name": "^0.0.1",
|
"nihilarr/parse-torrent-name": "^0.0.1",
|
||||||
"nyholm/psr7": "*",
|
"nyholm/psr7": "*",
|
||||||
"p3k/emoji-detector": "^1.2",
|
"p3k/emoji-detector": "^1.2",
|
||||||
@@ -29,6 +29,7 @@
|
|||||||
"symfony/mercure-bundle": "^0.3.9",
|
"symfony/mercure-bundle": "^0.3.9",
|
||||||
"symfony/messenger": "7.2.*",
|
"symfony/messenger": "7.2.*",
|
||||||
"symfony/runtime": "7.2.*",
|
"symfony/runtime": "7.2.*",
|
||||||
|
"symfony/scheduler": "7.2.*",
|
||||||
"symfony/security-bundle": "7.2.*",
|
"symfony/security-bundle": "7.2.*",
|
||||||
"symfony/stimulus-bundle": "^2.24",
|
"symfony/stimulus-bundle": "^2.24",
|
||||||
"symfony/twig-bundle": "7.2.*",
|
"symfony/twig-bundle": "7.2.*",
|
||||||
|
|||||||
583
composer.lock
generated
583
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,19 @@
|
|||||||
framework:
|
framework:
|
||||||
messenger:
|
messenger:
|
||||||
# Uncomment this (and the failed transport below) to send failed messages to this transport for later handling.
|
# Uncomment this (and the failed transport below) to send failed messages to this transport for later handling.
|
||||||
# failure_transport: failed
|
failure_transport: failed
|
||||||
|
|
||||||
transports:
|
transports:
|
||||||
# https://symfony.com/doc/current/messenger.html#transport-configuration
|
# https://symfony.com/doc/current/messenger.html#transport-configuration
|
||||||
async: '%env(MESSENGER_TRANSPORT_DSN)%'
|
async:
|
||||||
|
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
|
||||||
|
options:
|
||||||
|
use_notify: true
|
||||||
|
check_delayed_interval: 60000
|
||||||
|
retry_strategy:
|
||||||
|
max_retries: 1
|
||||||
|
multiplier: 1
|
||||||
|
|
||||||
failed: 'doctrine://default?queue_name=failed'
|
failed: 'doctrine://default?queue_name=failed'
|
||||||
|
|
||||||
default_bus: messenger.bus.default
|
default_bus: messenger.bus.default
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
use App\Download\Action\Command\AddMovieMonitorCommand;
|
use App\Download\Action\Command\MonitorMovieCommand;
|
||||||
use App\Download\Action\Handler\AddMovieMonitorHandler;
|
use App\Download\Action\Handler\AddMovieMonitorHandler;
|
||||||
|
use App\Download\Action\Handler\MonitorMovieHandler;
|
||||||
use App\Download\Action\Input\AddMovieMonitorInput;
|
use App\Download\Action\Input\AddMovieMonitorInput;
|
||||||
use App\Download\Action\Input\DownloadMediaInput;
|
use App\Download\Action\Input\DownloadMediaInput;
|
||||||
use App\Download\Framework\Repository\DownloadRepository;
|
use App\Download\Framework\Repository\DownloadRepository;
|
||||||
@@ -19,6 +20,18 @@ class DownloadController extends AbstractController
|
|||||||
private MessageBusInterface $bus,
|
private MessageBusInterface $bus,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
#[Route('/test', name: 'app_test')]
|
||||||
|
public function test(
|
||||||
|
MonitorMovieHandler $handler,
|
||||||
|
) {
|
||||||
|
$command = new MonitorMovieCommand(41);
|
||||||
|
$handler->handle($command);
|
||||||
|
return $this->json([
|
||||||
|
'status' => 200,
|
||||||
|
'message' => $command
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
#[Route('/download', name: 'app_download', methods: ['POST'])]
|
#[Route('/download', name: 'app_download', methods: ['POST'])]
|
||||||
public function download(
|
public function download(
|
||||||
DownloadMediaInput $input,
|
DownloadMediaInput $input,
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Download\Action\Command;
|
namespace App\Download\Action\Command;
|
||||||
|
|
||||||
use App\Download\Framework\Entity\MovieMonitor;
|
|
||||||
use OneToMany\RichBundle\Contract\CommandInterface;
|
use OneToMany\RichBundle\Contract\CommandInterface;
|
||||||
|
|
||||||
class MonitorMovieCommand implements CommandInterface
|
class MonitorMovieCommand implements CommandInterface
|
||||||
|
|||||||
@@ -10,9 +10,11 @@ use App\Download\Service\MonitorOptionEvaluator;
|
|||||||
use App\Torrentio\Action\Command\GetMovieOptionsCommand;
|
use App\Torrentio\Action\Command\GetMovieOptionsCommand;
|
||||||
use App\Torrentio\Action\Handler\GetMovieOptionsHandler;
|
use App\Torrentio\Action\Handler\GetMovieOptionsHandler;
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
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;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Component\Messenger\MessageBusInterface;
|
use Symfony\Component\Messenger\MessageBusInterface;
|
||||||
|
|
||||||
/** @implements HandlerInterface<MonitorMovieCommand> */
|
/** @implements HandlerInterface<MonitorMovieCommand> */
|
||||||
@@ -22,22 +24,31 @@ readonly class MonitorMovieHandler implements HandlerInterface
|
|||||||
private MovieMonitorRepository $movieMonitorRepository,
|
private MovieMonitorRepository $movieMonitorRepository,
|
||||||
private GetMovieOptionsHandler $getMovieOptionsHandler,
|
private GetMovieOptionsHandler $getMovieOptionsHandler,
|
||||||
private MonitorOptionEvaluator $monitorOptionEvaluator,
|
private MonitorOptionEvaluator $monitorOptionEvaluator,
|
||||||
|
private EntityManagerInterface $entityManager,
|
||||||
private MessageBusInterface $bus,
|
private MessageBusInterface $bus,
|
||||||
|
private LoggerInterface $logger,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function handle(CommandInterface $command): ResultInterface
|
public function handle(CommandInterface $command): ResultInterface
|
||||||
{
|
{
|
||||||
|
$this->logger->info('> [MonitorMovieHandler] Executing MonitorMovieHandler');
|
||||||
$monitor = $this->movieMonitorRepository->find($command->movieMonitorId);
|
$monitor = $this->movieMonitorRepository->find($command->movieMonitorId);
|
||||||
|
$monitor->setStatus('In Progress');
|
||||||
|
|
||||||
|
$this->logger->info('> [MonitorMovieHandler] Searching for "' . $monitor->getTitle() . '" download options');
|
||||||
|
|
||||||
$results = $this->getMovieOptionsHandler->handle(
|
$results = $this->getMovieOptionsHandler->handle(
|
||||||
new GetMovieOptionsCommand($monitor->getTmdbId(), $monitor->getImdbId())
|
new GetMovieOptionsCommand($monitor->getTmdbId(), $monitor->getImdbId())
|
||||||
);
|
);
|
||||||
|
$this->logger->info('> [MonitorMovieHandler] Found ' . count($results->results) . ' download options');
|
||||||
|
|
||||||
$result = $this->monitorOptionEvaluator->evaluateOptions($monitor, $results->results);
|
$result = $this->monitorOptionEvaluator->evaluateOptions($monitor, $results->results);
|
||||||
|
|
||||||
if (null !== $result) {
|
if (null !== $result) {
|
||||||
|
$this->logger->info('> [MonitorMovieHandler] 1 result found: dispatching DownloadMediaCommand for "' . $result->title . '"');
|
||||||
$this->bus->dispatch(new DownloadMediaCommand(
|
$this->bus->dispatch(new DownloadMediaCommand(
|
||||||
$result->url,
|
$result->url,
|
||||||
$result->title,
|
$monitor->getTitle(),
|
||||||
$result->filename,
|
$result->filename,
|
||||||
'movies',
|
'movies',
|
||||||
$monitor->getImdbId(),
|
$monitor->getImdbId(),
|
||||||
@@ -48,7 +59,7 @@ readonly class MonitorMovieHandler implements HandlerInterface
|
|||||||
|
|
||||||
$monitor->setLastSearch(new DateTimeImmutable());
|
$monitor->setLastSearch(new DateTimeImmutable());
|
||||||
$monitor->setSearchCount($monitor->getSearchCount() + 1);
|
$monitor->setSearchCount($monitor->getSearchCount() + 1);
|
||||||
$this->movieMonitorRepository->getEntityManager()->flush();
|
$this->entityManager->flush();
|
||||||
|
|
||||||
return new MonitorMovieResult(
|
return new MonitorMovieResult(
|
||||||
status: 'OK',
|
status: 'OK',
|
||||||
|
|||||||
@@ -14,18 +14,18 @@ class AddMovieMonitorInput implements InputInterface
|
|||||||
#[SourceSecurity]
|
#[SourceSecurity]
|
||||||
public string $userEmail,
|
public string $userEmail,
|
||||||
|
|
||||||
#[SourceRoute('title')]
|
|
||||||
public string $title,
|
|
||||||
|
|
||||||
#[SourceRoute('tmdbId')]
|
#[SourceRoute('tmdbId')]
|
||||||
public string $tmdbId,
|
public string $tmdbId,
|
||||||
|
|
||||||
#[SourceRoute('imdbId')]
|
#[SourceRoute('imdbId')]
|
||||||
public string $imdbId,
|
public string $imdbId,
|
||||||
|
|
||||||
|
#[SourceRoute('title')]
|
||||||
|
public string $title,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function toCommand(): CommandInterface
|
public function toCommand(): CommandInterface
|
||||||
{
|
{
|
||||||
return new AddMovieMonitorCommand($this->userEmail, $this->title, $this->tmdbId, $this->imdbId);
|
return new AddMovieMonitorCommand($this->userEmail, $this->title, $this->imdbId, $this->tmdbId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Download\Framework\MessageHandler;
|
|
||||||
|
|
||||||
use App\Download\Action\Command\DownloadMediaCommand;
|
|
||||||
use App\Download\Action\Handler\DownloadMediaHandler;
|
|
||||||
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
|
|
||||||
|
|
||||||
#[AsMessageHandler(handles: DownloadMediaCommand::class)]
|
|
||||||
class DownloadMediaMessageHandler
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
private DownloadMediaHandler $downloadMediaHandler,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public function __invoke(DownloadMediaCommand $command)
|
|
||||||
{
|
|
||||||
$this->downloadMediaHandler->handle($command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
29
src/Download/Framework/Scheduler/MovieMonitorDispatcher.php
Normal file
29
src/Download/Framework/Scheduler/MovieMonitorDispatcher.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Download\Framework\Scheduler;
|
||||||
|
|
||||||
|
use App\Download\Action\Command\MonitorMovieCommand;
|
||||||
|
use App\Download\Framework\Repository\MovieMonitorRepository;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Symfony\Component\Messenger\MessageBusInterface;
|
||||||
|
use Symfony\Component\Scheduler\Attribute\AsCronTask;
|
||||||
|
|
||||||
|
#[AsCronTask('* * * * *', schedule: 'movie_monitor')]
|
||||||
|
class MovieMonitorDispatcher
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly LoggerInterface $logger,
|
||||||
|
private readonly MovieMonitorRepository $movieMonitorRepository,
|
||||||
|
private readonly MessageBusInterface $bus,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function __invoke() {
|
||||||
|
$this->logger->info('[MovieMonitorDispatcher] Executing MovieMonitorDispatcher');
|
||||||
|
$monitors = $this->movieMonitorRepository->findBy(['status' => ['New', 'In Progress']]);
|
||||||
|
|
||||||
|
foreach ($monitors as $monitor) {
|
||||||
|
$this->logger->info('[MovieMonitorDispatcher] Dispatching MovieMonitorCommand for ' . $monitor->getTitle());
|
||||||
|
$this->bus->dispatch(new MonitorMovieCommand($monitor->getId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -53,7 +53,6 @@ class MonitorOptionEvaluator
|
|||||||
$sizeMatches = [];
|
$sizeMatches = [];
|
||||||
|
|
||||||
foreach ($bestMatches as $result) {
|
foreach ($bestMatches as $result) {
|
||||||
$size = 0;
|
|
||||||
if (str_contains($result->size, 'GB')) {
|
if (str_contains($result->size, 'GB')) {
|
||||||
$size = (int) trim(str_replace('GB', '', $result->size)) * 1024;
|
$size = (int) trim(str_replace('GB', '', $result->size)) * 1024;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
29
src/Schedule.php
Normal file
29
src/Schedule.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use App\Download\Framework\Scheduler\MovieMonitorDispatcher;
|
||||||
|
use Symfony\Component\Scheduler\Attribute\AsSchedule;
|
||||||
|
use Symfony\Component\Scheduler\Schedule as SymfonySchedule;
|
||||||
|
use Symfony\Component\Scheduler\ScheduleProviderInterface;
|
||||||
|
use Symfony\Contracts\Cache\CacheInterface;
|
||||||
|
|
||||||
|
#[AsSchedule]
|
||||||
|
class Schedule implements ScheduleProviderInterface
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private CacheInterface $cache,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSchedule(): SymfonySchedule
|
||||||
|
{
|
||||||
|
return (new SymfonySchedule())
|
||||||
|
->stateful($this->cache) // ensure missed tasks are executed
|
||||||
|
->processOnlyLastMissedRun(true) // ensure only last missed task is run
|
||||||
|
|
||||||
|
// add your own tasks here
|
||||||
|
// see https://symfony.com/doc/current/scheduler.html#attaching-recurring-messages-to-a-schedule
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
namespace App\Tmdb;
|
namespace App\Tmdb;
|
||||||
|
|
||||||
use App\Enum\MediaType;
|
use App\Enum\MediaType;
|
||||||
use App\Tmdb\TmdbResult;
|
|
||||||
use App\ValueObject\ResultFactory;
|
use App\ValueObject\ResultFactory;
|
||||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||||
@@ -18,12 +17,8 @@ use Tmdb\Event\Listener\Request\ContentTypeJsonRequestListener;
|
|||||||
use Tmdb\Event\Listener\Request\UserAgentRequestListener;
|
use Tmdb\Event\Listener\Request\UserAgentRequestListener;
|
||||||
use Tmdb\Event\Listener\RequestListener;
|
use Tmdb\Event\Listener\RequestListener;
|
||||||
use Tmdb\Event\RequestEvent;
|
use Tmdb\Event\RequestEvent;
|
||||||
use Tmdb\Model\AbstractModel;
|
|
||||||
use Tmdb\Model\Image;
|
|
||||||
use Tmdb\Model\Movie;
|
use Tmdb\Model\Movie;
|
||||||
use Tmdb\Model\Movie\QueryParameter\AppendToResponse;
|
|
||||||
use Tmdb\Model\Search\SearchQuery\KeywordSearchQuery;
|
use Tmdb\Model\Search\SearchQuery\KeywordSearchQuery;
|
||||||
use Tmdb\Model\Search\SearchQuery\MovieSearchQuery;
|
|
||||||
use Tmdb\Model\Tv;
|
use Tmdb\Model\Tv;
|
||||||
use Tmdb\Repository\MovieRepository;
|
use Tmdb\Repository\MovieRepository;
|
||||||
use Tmdb\Repository\SearchRepository;
|
use Tmdb\Repository\SearchRepository;
|
||||||
@@ -210,7 +205,19 @@ class Tmdb
|
|||||||
throw new \Exception("A media type must be set when parsing from an array.");
|
throw new \Exception("A media type must be set when parsing from an array.");
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTvShow(array $data, string $posterBasePath): TmdbResult {
|
if ($mediaType === 'movie') {
|
||||||
|
$result = $this->parseMovie($data, self::POSTER_IMG_PATH);
|
||||||
|
} elseif ($mediaType === 'tvshow') {
|
||||||
|
$result = $this->parseTvShow($data, self::POSTER_IMG_PATH);
|
||||||
|
} elseif ($mediaType === 'episode') {
|
||||||
|
$result = $this->parseEpisode($data, self::POSTER_IMG_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function parseTvShow(array $data, string $posterBasePath): TmdbResult
|
||||||
|
{
|
||||||
return new TmdbResult(
|
return new TmdbResult(
|
||||||
imdbId: $data['external_ids']['imdb_id'],
|
imdbId: $data['external_ids']['imdb_id'],
|
||||||
tmdbId: $data['id'],
|
tmdbId: $data['id'],
|
||||||
@@ -223,7 +230,8 @@ class Tmdb
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseEpisode(array $data, string $posterBasePath): TmdbResult {
|
private function parseEpisode(array $data, string $posterBasePath): TmdbResult
|
||||||
|
{
|
||||||
return new TmdbResult(
|
return new TmdbResult(
|
||||||
imdbId: $data['external_ids']['imdb_id'],
|
imdbId: $data['external_ids']['imdb_id'],
|
||||||
tmdbId: $data['id'],
|
tmdbId: $data['id'],
|
||||||
@@ -236,7 +244,8 @@ class Tmdb
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseMovie(array $data, string $posterBasePath): TmdbResult {
|
private function parseMovie(array $data, string $posterBasePath): TmdbResult
|
||||||
|
{
|
||||||
return new TmdbResult(
|
return new TmdbResult(
|
||||||
imdbId: $data['external_ids']['imdb_id'],
|
imdbId: $data['external_ids']['imdb_id'],
|
||||||
tmdbId: $data['id'],
|
tmdbId: $data['id'],
|
||||||
@@ -248,16 +257,6 @@ class Tmdb
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function parseFromObject($result): TmdbResult
|
private function parseFromObject($result): TmdbResult
|
||||||
{
|
{
|
||||||
|
|||||||
12
symfony.lock
12
symfony.lock
@@ -158,6 +158,18 @@
|
|||||||
"config/routes.yaml"
|
"config/routes.yaml"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"symfony/scheduler": {
|
||||||
|
"version": "7.2",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes",
|
||||||
|
"branch": "main",
|
||||||
|
"version": "7.2",
|
||||||
|
"ref": "caea3c928ee9e1b21288fd76aef36f16ea355515"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"src/Schedule.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
"symfony/security-bundle": {
|
"symfony/security-bundle": {
|
||||||
"version": "7.2",
|
"version": "7.2",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
|
|||||||
@@ -16,9 +16,8 @@
|
|||||||
</h3>
|
</h3>
|
||||||
<button class="px-1.5 py-1 bg-green-600 text-white rounded-md cursor-pointer"
|
<button class="px-1.5 py-1 bg-green-600 text-white rounded-md cursor-pointer"
|
||||||
{{ stimulus_controller('monitor', {
|
{{ stimulus_controller('monitor', {
|
||||||
mediaType: results.media.mediaType,
|
|
||||||
imdbId: results.media.imdbId,
|
|
||||||
tmdbId: results.media.tmdbId,
|
tmdbId: results.media.tmdbId,
|
||||||
|
imdbId: results.media.imdbId,
|
||||||
title: results.media.title
|
title: results.media.title
|
||||||
}) }}
|
}) }}
|
||||||
{% if results.media.mediaType == "movies" %}
|
{% if results.media.mediaType == "movies" %}
|
||||||
|
|||||||
Reference in New Issue
Block a user