Compare commits
9 Commits
dev-notifi
...
v0.31.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 502b85dda4 | |||
| 9c430290e9 | |||
| 583591bf4f | |||
| 182708b8f0 | |||
| d6ba4d7d2a | |||
| e5c5ec93a8 | |||
| 942911d8ef | |||
| 2f7d406d12 | |||
| 4e1adc576c |
2
.env
2
.env
@@ -57,4 +57,4 @@ OIDC_BYPASS_FORM_LOGIN=false
|
|||||||
###< symfony/ntfy-notifier ###
|
###< symfony/ntfy-notifier ###
|
||||||
|
|
||||||
NOTIFICATION_TRANSPORT=
|
NOTIFICATION_TRANSPORT=
|
||||||
NTFY_DNS=
|
NTFY_DSN=
|
||||||
|
|||||||
@@ -18,11 +18,3 @@ var observer = new MutationObserver(function(mutations) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
observer.observe(document, {attributes: false, childList: true, characterData: false, subtree:true});
|
observer.observe(document, {attributes: false, childList: true, characterData: false, subtree:true});
|
||||||
|
|
||||||
const ptr = PullToRefresh.init({
|
|
||||||
mainElement: 'body',
|
|
||||||
onRefresh() {
|
|
||||||
window.location.reload();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|||||||
57
assets/controllers/upcoming_episodes_controller.js
Normal file
57
assets/controllers/upcoming_episodes_controller.js
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { Controller } from '@hotwired/stimulus';
|
||||||
|
import { Calendar } from '@fullcalendar/core';
|
||||||
|
import dayGridPlugin from '@fullcalendar/daygrid';
|
||||||
|
import timeGridPlugin from '@fullcalendar/timegrid';
|
||||||
|
|
||||||
|
|
||||||
|
/* stimulusFetch: 'lazy' */
|
||||||
|
export default class extends Controller {
|
||||||
|
calendar = null;
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
connect() {
|
||||||
|
this.calendar = new Calendar(this.element, {
|
||||||
|
plugins: [ dayGridPlugin, timeGridPlugin ],
|
||||||
|
initialView: 'dayGridMonth',
|
||||||
|
headerToolbar: {
|
||||||
|
left: 'prev,next today',
|
||||||
|
center: 'title',
|
||||||
|
right: 'dayGridMonth,timeGridWeek,timeGridDay'
|
||||||
|
},
|
||||||
|
editable: true, // Allow events to be dragged and resized
|
||||||
|
events: '/api/events', // Symfony route to fetch events
|
||||||
|
eventDrop: function(info) {
|
||||||
|
// Handle event drop (e.g., update event in database via AJAX)
|
||||||
|
},
|
||||||
|
eventResize: function(info) {
|
||||||
|
// Handle event resize (e.g., update event in database via AJAX)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.calendar.render();
|
||||||
|
// this.calendar = new Calendar(this.element, {
|
||||||
|
// plugins: [ dayGridPlugin, timeGridPlugin, listPlugin ],
|
||||||
|
// initialView: 'dayGridMonth',
|
||||||
|
// headerToolbar: {
|
||||||
|
// left: 'prev,next today',
|
||||||
|
// center: 'title',
|
||||||
|
// right: 'dayGridMonth,timeGridWeek,listWeek'
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// this.calendar.render();
|
||||||
|
// calendar.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add custom controller actions here
|
||||||
|
// fooBar() { this.fooTarget.classList.toggle(this.bazClass) }
|
||||||
|
|
||||||
|
disconnect() {
|
||||||
|
// Called anytime its element is disconnected from the DOM
|
||||||
|
// (on page change, when it's removed from or moved in the DOM, etc.)
|
||||||
|
|
||||||
|
// Here you should remove all event listeners added in "connect()"
|
||||||
|
// this.fooTarget.removeEventListener('click', this._fooBar)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,6 +27,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--fc-border-color: #a65b27;
|
||||||
|
--fc-page-bg-color: #a65b27;
|
||||||
|
}
|
||||||
|
|
||||||
/* Prevent scrolling while dialog is open */
|
/* Prevent scrolling while dialog is open */
|
||||||
body:has(dialog[data-dialog-target="dialog"][open]) {
|
body:has(dialog[data-dialog-target="dialog"][open]) {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -193,3 +198,7 @@ dialog[data-dialog-target="dialog"][closing] {
|
|||||||
.filter-label {
|
.filter-label {
|
||||||
@apply flex flex-col gap-1 justify-between;
|
@apply flex flex-col gap-1 justify-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fc-col-header-cell {
|
||||||
|
@apply bg-orange-500/60 text-white;
|
||||||
|
}
|
||||||
|
|||||||
35
migrations/Version20250823173128.php
Normal file
35
migrations/Version20250823173128.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated Migration: Please modify to your needs!
|
||||||
|
*/
|
||||||
|
final class Version20250823173128 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this up() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql(<<<'SQL'
|
||||||
|
ALTER TABLE monitor ADD air_date DATETIME DEFAULT NULL
|
||||||
|
SQL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql(<<<'SQL'
|
||||||
|
ALTER TABLE monitor DROP air_date
|
||||||
|
SQL);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -53,9 +53,6 @@ final class IndexController extends AbstractController
|
|||||||
#[Route('/test')]
|
#[Route('/test')]
|
||||||
public function monitorTvShow(): Response
|
public function monitorTvShow(): Response
|
||||||
{
|
{
|
||||||
$this->monitorTvShowHandler->handle(new MonitorTvShowCommand(96));
|
return $this->render('index/test.html.twig', []);
|
||||||
return $this->json([
|
|
||||||
'Success' => 'Monitor added'
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ namespace App\Monitor\Action\Handler;
|
|||||||
use App\Base\Util\EpisodeId;
|
use App\Base\Util\EpisodeId;
|
||||||
use App\Download\Action\Command\DownloadMediaCommand;
|
use App\Download\Action\Command\DownloadMediaCommand;
|
||||||
use App\Download\DownloadOptionEvaluator;
|
use App\Download\DownloadOptionEvaluator;
|
||||||
use App\Download\Framework\Entity\Download;
|
|
||||||
use App\Download\Framework\Repository\DownloadRepository;
|
use App\Download\Framework\Repository\DownloadRepository;
|
||||||
use App\Monitor\Action\Command\MonitorMovieCommand;
|
use App\Monitor\Action\Command\MonitorMovieCommand;
|
||||||
use App\Monitor\Action\Result\MonitorTvEpisodeResult;
|
use App\Monitor\Action\Result\MonitorTvEpisodeResult;
|
||||||
@@ -44,6 +43,11 @@ readonly class MonitorTvEpisodeHandler implements HandlerInterface
|
|||||||
$this->logger->info('> [MonitorTvEpisodeHandler] Executing MonitorTvEpisodeHandler for ' . $monitor->getTitle() . ' season ' . $monitor->getSeason() . ' episode ' . $monitor->getEpisode());
|
$this->logger->info('> [MonitorTvEpisodeHandler] Executing MonitorTvEpisodeHandler for ' . $monitor->getTitle() . ' season ' . $monitor->getSeason() . ' episode ' . $monitor->getEpisode());
|
||||||
|
|
||||||
$episodeData = $this->tmdb->episodeDetails($monitor->getTmdbId(), $monitor->getSeason(), $monitor->getEpisode());
|
$episodeData = $this->tmdb->episodeDetails($monitor->getTmdbId(), $monitor->getSeason(), $monitor->getEpisode());
|
||||||
|
|
||||||
|
if (null === $monitor->getAirDate() && null !== $episodeData->episodeAirDate && "" !== $episodeData->episodeAirDate) {
|
||||||
|
$monitor->setAirDate(Carbon::parse($episodeData->episodeAirDate));
|
||||||
|
}
|
||||||
|
|
||||||
if (Carbon::createFromTimestamp($episodeData->episodeAirDate) > Carbon::today('UTC')) {
|
if (Carbon::createFromTimestamp($episodeData->episodeAirDate) > Carbon::today('UTC')) {
|
||||||
$this->logger->info('> [MonitorTvEpisodeHandler] ...Episode has not aired yet, skipping for now');
|
$this->logger->info('> [MonitorTvEpisodeHandler] ...Episode has not aired yet, skipping for now');
|
||||||
return new MonitorTvEpisodeResult(
|
return new MonitorTvEpisodeResult(
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use App\Monitor\Framework\Entity\Monitor;
|
|||||||
use App\Monitor\Framework\Repository\MonitorRepository;
|
use App\Monitor\Framework\Repository\MonitorRepository;
|
||||||
use App\Tmdb\Tmdb;
|
use App\Tmdb\Tmdb;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
use Carbon\CarbonImmutable;
|
||||||
use DateTimeImmutable;
|
use DateTimeImmutable;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use App\Base\Util\PTN;
|
use App\Base\Util\PTN;
|
||||||
@@ -96,6 +97,7 @@ readonly class MonitorTvShowHandler implements HandlerInterface
|
|||||||
->setMonitorType('tvepisode')
|
->setMonitorType('tvepisode')
|
||||||
->setSeason($episode['season_number'])
|
->setSeason($episode['season_number'])
|
||||||
->setEpisode($episode['episode_number'])
|
->setEpisode($episode['episode_number'])
|
||||||
|
->setAirDate($episode['air_date'] !== null && $episode['air_date'] !== "" ? Carbon::parse($episode['air_date']) : null)
|
||||||
->setCreatedAt(new DateTimeImmutable())
|
->setCreatedAt(new DateTimeImmutable())
|
||||||
->setSearchCount(0)
|
->setSearchCount(0)
|
||||||
->setStatus('New');
|
->setStatus('New');
|
||||||
|
|||||||
@@ -2,11 +2,13 @@
|
|||||||
|
|
||||||
namespace App\Monitor\Framework\Controller;
|
namespace App\Monitor\Framework\Controller;
|
||||||
|
|
||||||
|
use Aimeos\Map;
|
||||||
use App\Base\Service\Broadcaster;
|
use App\Base\Service\Broadcaster;
|
||||||
use App\Monitor\Action\Handler\AddMonitorHandler;
|
use App\Monitor\Action\Handler\AddMonitorHandler;
|
||||||
use App\Monitor\Action\Handler\DeleteMonitorHandler;
|
use App\Monitor\Action\Handler\DeleteMonitorHandler;
|
||||||
use App\Monitor\Action\Input\AddMonitorInput;
|
use App\Monitor\Action\Input\AddMonitorInput;
|
||||||
use App\Monitor\Action\Input\DeleteMonitorInput;
|
use App\Monitor\Action\Input\DeleteMonitorInput;
|
||||||
|
use App\Monitor\Framework\Repository\MonitorRepository;
|
||||||
use App\Monitor\Framework\Scheduler\MonitorDispatcher;
|
use App\Monitor\Framework\Scheduler\MonitorDispatcher;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
@@ -65,4 +67,45 @@ class ApiController extends AbstractController
|
|||||||
'message' => 'Manually dispatched MonitorDispatcher'
|
'message' => 'Manually dispatched MonitorDispatcher'
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Route('/api/monitor/upcoming-episodes', name: 'api.monitor.upcoming-episodes', methods: ['GET'])]
|
||||||
|
public function upcomingEpisodes(MonitorRepository $repository): Response
|
||||||
|
{
|
||||||
|
$colors = [
|
||||||
|
'blue' => '#007bff',
|
||||||
|
'indigo' => '#6610f2',
|
||||||
|
'purple' => '#6f42c1',
|
||||||
|
'pink' => '#e83e8c',
|
||||||
|
'red' => '#dc3545',
|
||||||
|
'orange' => '#fd7e14',
|
||||||
|
'yellow' => '#ffc107',
|
||||||
|
'green' => '#28a745',
|
||||||
|
'teal' => '#20c997',
|
||||||
|
'cyan' => '#17a2b8',
|
||||||
|
];
|
||||||
|
|
||||||
|
$eventColors = [];
|
||||||
|
$monitors = $repository->whereAirDateNotNull();
|
||||||
|
$monitors = Map::from($monitors)->map(function ($monitor) use (&$eventColors, $colors) {
|
||||||
|
if (!array_key_exists($monitor->getImdbId(), $eventColors)) {
|
||||||
|
$eventColors[$monitor->getImdbId()] = $colors[array_rand($colors)];
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
'id' => $monitor->getId(),
|
||||||
|
'title' => $monitor->getTitle() . ' (S' . str_pad($monitor->getSeason(), 2, '0', STR_PAD_LEFT) . 'E' . str_pad($monitor->getEpisode(), 2, '0', STR_PAD_LEFT) . ')',
|
||||||
|
'start' => $monitor->getAirDate()->format('Y-m-d H:i:s'),
|
||||||
|
'groupId' => $monitor->getImdbId(),
|
||||||
|
'allDay' => true,
|
||||||
|
'backgroundColor' => $eventColors[$monitor->getImdbId()],
|
||||||
|
'borderColor' => $eventColors[$monitor->getImdbId()],
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
return $this->json([
|
||||||
|
'status' => 200,
|
||||||
|
'data' => [
|
||||||
|
'episodes' => $monitors->toArray(),
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,4 +28,10 @@ class WebController extends AbstractController
|
|||||||
{
|
{
|
||||||
return $this->render('monitor/index.html.twig');
|
return $this->render('monitor/index.html.twig');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Route('/monitors/upcoming-episodes', name: 'app.monitor.upcoming-episodes', methods: ['GET'])]
|
||||||
|
public function upcomingEpisodes()
|
||||||
|
{
|
||||||
|
return $this->render('monitor/upcoming-episodes.html.twig');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ class Monitor
|
|||||||
#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
|
#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
|
||||||
private ?\DateTimeInterface $lastSearch = null;
|
private ?\DateTimeInterface $lastSearch = null;
|
||||||
|
|
||||||
|
#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
|
||||||
|
private ?\DateTime $airDate = null;
|
||||||
|
|
||||||
#[ORM\Column]
|
#[ORM\Column]
|
||||||
private ?\DateTimeImmutable $createdAt = null;
|
private ?\DateTimeImmutable $createdAt = null;
|
||||||
|
|
||||||
@@ -257,6 +260,17 @@ class Monitor
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getAirDate(): ?\DateTimeInterface
|
||||||
|
{
|
||||||
|
return $this->airDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setAirDate(?\DateTimeInterface $airDate): static
|
||||||
|
{
|
||||||
|
$this->airDate = $airDate;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function removeChild(self $child): static
|
public function removeChild(self $child): static
|
||||||
{
|
{
|
||||||
if ($this->children->removeElement($child)) {
|
if ($this->children->removeElement($child)) {
|
||||||
|
|||||||
@@ -33,4 +33,12 @@ class MonitorRepository extends ServiceEntityRepository
|
|||||||
|
|
||||||
return $this->paginator->paginate($query, $page, $perPage);
|
return $this->paginator->paginate($query, $page, $perPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function whereAirDateNotNull()
|
||||||
|
{
|
||||||
|
$query = $this->createQueryBuilder('m')
|
||||||
|
->andWhere('m.airDate IS NOT NULL')
|
||||||
|
->getQuery();
|
||||||
|
return $query->getResult();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -286,13 +286,18 @@ class Tmdb
|
|||||||
|
|
||||||
private function parseTvShow(array $data, string $posterBasePath): TmdbResult
|
private function parseTvShow(array $data, string $posterBasePath): TmdbResult
|
||||||
{
|
{
|
||||||
|
if (!in_array($data['first_air_date'], ['', null,])) {
|
||||||
|
$airDate = (new \DateTime($data['first_air_date']))->format('Y-m-d');
|
||||||
|
} else {
|
||||||
|
$airDate = null;
|
||||||
|
}
|
||||||
return new TmdbResult(
|
return new TmdbResult(
|
||||||
imdbId: $data['external_ids']['imdb_id'],
|
imdbId: $data['external_ids']['imdb_id'],
|
||||||
tmdbId: $data['id'],
|
tmdbId: $data['id'],
|
||||||
title: $data['name'],
|
title: $data['name'],
|
||||||
poster: (null !== $data['poster_path']) ? $posterBasePath . $data['poster_path'] : null,
|
poster: (null !== $data['poster_path']) ? $posterBasePath . $data['poster_path'] : null,
|
||||||
description: $data['overview'],
|
description: $data['overview'],
|
||||||
year: (new \DateTime($data['first_air_date']))->format('Y'),
|
year: $airDate,
|
||||||
mediaType: "tvshows",
|
mediaType: "tvshows",
|
||||||
episodes: $data['episodes'],
|
episodes: $data['episodes'],
|
||||||
);
|
);
|
||||||
@@ -300,6 +305,11 @@ class Tmdb
|
|||||||
|
|
||||||
private function parseEpisode(array $data, string $posterBasePath): TmdbResult
|
private function parseEpisode(array $data, string $posterBasePath): TmdbResult
|
||||||
{
|
{
|
||||||
|
if (!in_array($data['air_date'], ['', null,])) {
|
||||||
|
$airDate = (new \DateTime($data['air_date']))->format('Y-m-d');
|
||||||
|
} else {
|
||||||
|
$airDate = null;
|
||||||
|
}
|
||||||
return new TmdbResult(
|
return new TmdbResult(
|
||||||
imdbId: $data['external_ids']['imdb_id'],
|
imdbId: $data['external_ids']['imdb_id'],
|
||||||
tmdbId: $data['id'],
|
tmdbId: $data['id'],
|
||||||
@@ -309,12 +319,17 @@ class Tmdb
|
|||||||
year: (new \DateTime($data['air_date']))->format('Y'),
|
year: (new \DateTime($data['air_date']))->format('Y'),
|
||||||
mediaType: "tvshows",
|
mediaType: "tvshows",
|
||||||
episodes: null,
|
episodes: null,
|
||||||
episodeAirDate: (new \DateTime($data['air_date']))->format('m/d/Y'),
|
episodeAirDate: $airDate,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function parseMovie(array $data, string $posterBasePath): TmdbResult
|
private function parseMovie(array $data, string $posterBasePath): TmdbResult
|
||||||
{
|
{
|
||||||
|
if (!in_array($data['release_date'], ['', null,])) {
|
||||||
|
$airDate = (new \DateTime($data['release_date']))->format('Y-m-d');
|
||||||
|
} else {
|
||||||
|
$airDate = null;
|
||||||
|
}
|
||||||
return new TmdbResult(
|
return new TmdbResult(
|
||||||
imdbId: $data['external_ids']['imdb_id'],
|
imdbId: $data['external_ids']['imdb_id'],
|
||||||
tmdbId: $data['id'],
|
tmdbId: $data['id'],
|
||||||
@@ -323,7 +338,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'),
|
episodeAirDate: $airDate,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
{% block javascripts %}
|
{% block javascripts %}
|
||||||
{% block importmap %}{{ importmap('app') }}{% endblock %}
|
{% block importmap %}{{ importmap('app') }}{% endblock %}
|
||||||
|
<script src='https://cdn.jsdelivr.net/npm/fullcalendar@6.1.19/index.global.min.js'></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
<body class="flex flex-col bg-stone-700">
|
<body class="flex flex-col bg-stone-700">
|
||||||
|
|||||||
9
templates/index/test.html.twig
Normal file
9
templates/index/test.html.twig
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{% extends 'base.html.twig' %}
|
||||||
|
|
||||||
|
{% block h2 %}Test Test{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<div>
|
||||||
|
<!-- Well what are you doing here? -->
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -4,6 +4,10 @@
|
|||||||
{% block h2 %}Monitors{% endblock %}
|
{% block h2 %}Monitors{% endblock %}
|
||||||
|
|
||||||
{% block action_buttons %}
|
{% block action_buttons %}
|
||||||
|
<a href="{{ path('app.monitor.upcoming-episodes') }}" data-turbo="false"
|
||||||
|
class="h-6 bg-orange-500/80 hover:bg-orange-600/80 px-2 text-white rounded-ms text-sm font-semibold">
|
||||||
|
Upcoming Episodes
|
||||||
|
</a>
|
||||||
<twig:ActionButton action="monitorDispatch" text="Run Monitors" />
|
<twig:ActionButton action="monitorDispatch" text="Run Monitors" />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|||||||
47
templates/monitor/upcoming-episodes.html.twig
Normal file
47
templates/monitor/upcoming-episodes.html.twig
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
{% extends 'base.html.twig' %}
|
||||||
|
|
||||||
|
{% block h2 %}Upcoming Episodes{% endblock %}
|
||||||
|
|
||||||
|
{% block action_buttons %}
|
||||||
|
<a href="{{ path('app.monitor.upcoming-episodes') }}"
|
||||||
|
class="h-6 bg-orange-500/80 hover:bg-orange-600/80 px-2 text-white rounded-ms text-sm font-semibold">
|
||||||
|
Upcoming Episodes
|
||||||
|
</a>
|
||||||
|
<twig:ActionButton action="monitorDispatch" text="Run Monitors" />
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
{{ turbo_page_requires_reload() }}
|
||||||
|
<script src='https://cdn.jsdelivr.net/npm/fullcalendar@6.1.19/index.global.min.js'></script>
|
||||||
|
|
||||||
|
<div class="p-4">
|
||||||
|
<twig:Card title="Upcoming episodes of shows your monitoring">
|
||||||
|
<div id="calendar" class="text-white">
|
||||||
|
</div>
|
||||||
|
</twig:Card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function getView() {
|
||||||
|
if (window.innerWidth < 768) {
|
||||||
|
return 'listWeek';
|
||||||
|
} else {
|
||||||
|
return 'dayGridMonth';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener('DOMContentLoaded', async function() {
|
||||||
|
let data = await fetch('/api/monitor/upcoming-episodes');
|
||||||
|
data = (await data.json())['data'];
|
||||||
|
|
||||||
|
const calendarEl = document.getElementById('calendar');
|
||||||
|
const calendar = new FullCalendar.Calendar(calendarEl, {
|
||||||
|
initialView: getView(),
|
||||||
|
events: data['episodes'],
|
||||||
|
windowResize: function(arg) {
|
||||||
|
this.changeView(getView());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
calendar.render();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user