Compare commits

...

4 Commits

Author SHA1 Message Date
Brock H Caldwell
ec0d2a198c feat: shows monitor air date 2025-11-05 23:49:13 -06:00
Brock H Caldwell
1f1c6f775f feat: adds poster to monitors & ical 2025-11-05 23:42:40 -06:00
Brock H Caldwell
cd14a197aa feat: stores poster with tv show monitors 2025-11-05 23:21:15 -06:00
Brock H Caldwell
a9031df3c3 feat: only shows top level monitors on dashboard and children on dedicated page 2025-11-05 22:35:12 -06:00
11 changed files with 115 additions and 2 deletions

View File

@@ -67,6 +67,9 @@ export default class MonitorListRow extends HTMLTableRowElement {
<th class="px-4 py-2">
<div class="dark:text-orange-500 text-right whitespace-nowrap ">Downloaded At</div>
</th>
<th class="px-4 py-2">
<div class="dark:text-orange-500 text-right whitespace-nowrap ">Air Date</div>
</th>
</tr>
</thead>
<tbody>
@@ -107,6 +110,9 @@ export default class MonitorListRow extends HTMLTableRowElement {
<td class="px-4 py-2">
<div class="text-left dark:text-white whitespace-nowrap font-normal">${this.getAttribute('downloaded-at') ?? "-"}</div>
</td>
<td class="px-4 py-2">
<div class="text-left dark:text-white whitespace-nowrap font-normal">${this.getAttribute('air-date') ?? "-"}</div>
</td>
</tr>
</tbody>
</table>

View File

@@ -23,7 +23,12 @@ export default class extends Controller {
}
if (null !== content && undefined !== content && "" !== content) {
document.dispatchEvent(new CustomEvent('showPreviewContentModal', {detail: {heading: heading, content: content}}))
if (['', null, undefined].includes(monitor.getAttribute('parent-id'))) {
window.location.href = `/monitors/${monitor.getAttribute('monitor-id')}`;
} else {
document.dispatchEvent(new CustomEvent('showPreviewContentModal', {detail: {heading: heading, content: content}}))
}
}
})
})

View File

@@ -0,0 +1,34 @@
<?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 Version20251106045808 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
$this->addSql(<<<'SQL'
ALTER TABLE monitor ADD poster VARCHAR(1024) 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 poster
SQL);
}
}

View File

@@ -8,6 +8,7 @@ use App\Monitor\Action\Result\AddMonitorResult;
use App\Monitor\Framework\Entity\Monitor;
use App\Monitor\Framework\Repository\MonitorRepository;
use App\Monitor\MonitorEvents;
use App\Tmdb\TmdbClient;
use App\User\Framework\Repository\UserRepository;
use DateTimeImmutable;
use OneToMany\RichBundle\Contract\CommandInterface;
@@ -22,13 +23,17 @@ readonly class AddMonitorHandler implements HandlerInterface
private MessageBusInterface $bus,
private MonitorRepository $movieMonitorRepository,
private UserRepository $userRepository,
private TmdbClient $tmdb,
) {}
public function handle(CommandInterface $command): ResultInterface
{
$user = $this->userRepository->find($command->userId);
$poster = $this->getPoster($command->imdbId);
$monitor = (new Monitor())
->setUser($user)
->setPoster($poster)
->setTmdbId($command->tmdbId)
->setImdbId($command->imdbId)
->setTitle($command->title)
@@ -56,4 +61,10 @@ readonly class AddMonitorHandler implements HandlerInterface
]
);
}
private function getPoster(string $imdbId): ?string
{
$data = $this->tmdb->tvShowDetails($imdbId);
return $data->poster;
}
}

View File

@@ -9,6 +9,7 @@ use App\Download\Framework\Repository\DownloadRepository;
use App\EventLog\Action\Command\AddEventLogCommand;
use App\Monitor\Action\Command\MonitorMovieCommand;
use App\Monitor\Action\Result\MonitorTvEpisodeResult;
use App\Monitor\Framework\Entity\Monitor;
use App\Monitor\Framework\Repository\MonitorRepository;
use App\Monitor\MonitorEvents;
use App\Tmdb\TmdbClient;
@@ -43,6 +44,7 @@ readonly class MonitorTvEpisodeHandler implements HandlerInterface
try {
$monitor = $this->monitorRepository->find($command->movieMonitorId);
$this->logger->info('> [MonitorTvEpisodeHandler] Executing MonitorTvEpisodeHandler for ' . $monitor->getTitle() . ' season ' . $monitor->getSeason() . ' episode ' . $monitor->getEpisode());
$this->refreshData($monitor);
$this->bus->dispatch(new AddEventLogCommand(
$monitor->getUser(),
@@ -151,4 +153,15 @@ readonly class MonitorTvEpisodeHandler implements HandlerInterface
]
);
}
private function refreshData(Monitor $monitor)
{
if (null === $monitor->getPoster()) {
$this->logger->info('> [MonitorTvEpisodeHandler] Refreshing poster for "' . $monitor->getTitle() . '"');
$poster = $monitor->getParent()->getPoster();
if (null !== $poster && "" !== $poster) {
$monitor->setPoster($poster);
}
}
}
}

View File

@@ -37,6 +37,7 @@ readonly class MonitorTvShowHandler implements HandlerInterface
{
$this->logger->info('> [MonitorTvShowHandler] Executing MonitorTvShowHandler');
$monitor = $this->monitorRepository->find($command->monitorId);
$this->refreshData($monitor);
// Check current episodes
$downloadedEpisodes = $this->mediaFiles
@@ -157,4 +158,15 @@ readonly class MonitorTvShowHandler implements HandlerInterface
'status' => ['New', 'Active', 'In Progress']
]) !== null;
}
private function refreshData(Monitor $monitor)
{
if (null === $monitor->getPoster()) {
$this->logger->info('> [MonitorTvShowHandler] Refreshing poster for "' . $monitor->getTitle() . '"');
$poster = $this->tmdb->tvshowDetails($monitor->getImdbId())->poster;
if (null !== $poster && "" !== $poster) {
$monitor->setPoster($poster);
}
}
}
}

View File

@@ -3,6 +3,7 @@
namespace App\Monitor\Framework\Controller;
use Aimeos\Map;
use App\Monitor\Framework\Entity\Monitor;
use App\Monitor\Framework\Repository\MonitorRepository;
use App\User\Framework\Entity\User;
use Spatie\IcalendarGenerator\Components\Calendar;
@@ -27,9 +28,10 @@ class CalendarController extends AbstractController
->refreshInterval(10);
$monitors = $monitorRepository->whereAirDateNotNull();
$calendar->event(Map::from($monitors)->map(function ($monitor) {
$calendar->event(Map::from($monitors)->map(function (Monitor $monitor) {
return new Event($monitor->getTitle())
->startsAt($monitor->getAirDate())
->attachment($monitor->getPoster())
->fullDay();
})->toArray());

View File

@@ -50,6 +50,9 @@ class Monitor
#[ORM\Column(nullable: true)]
private ?int $searchCount = null;
#[ORM\Column(nullable: true)]
private ?string $poster = null;
#[ORM\Column]
private bool $onlyFuture = true;
@@ -230,6 +233,17 @@ class Monitor
return $this;
}
public function getPoster(): ?string
{
return $this->poster;
}
public function setPoster(?string $poster): ?self
{
$this->poster = $poster;
return $this;
}
public function getParent(): ?self
{
return $this->parent;

View File

@@ -53,6 +53,7 @@ final class MonitorList extends AbstractController
return $this->asPaginator($this->monitorRepository->createQueryBuilder('m')
->andWhere('m.status IN (:statuses)')
->andWhere('(m.title LIKE :term OR m.imdbId LIKE :term OR m.monitorType LIKE :term OR m.status LIKE :term)')
->andWhere('m.parent IS NULL')
->setParameter('statuses', ['New', 'In Progress', 'Active'])
->setParameter('term', '%'.$this->term.'%')
->orderBy('m.id', 'DESC')

View File

@@ -17,10 +17,17 @@
class="px-6 py-3 text-start">
ID
</th>
{% if null != parentMonitorId %}
<th scope="col"
class="hidden md:table-cell px-6 py-3 text-start">
Search Count
</th>
{% else %}
<th scope="col"
class="hidden md:table-cell px-6 py-3 text-start">
Episodes
</th>
{% endif %}
<th scope="col"
class="hidden md:table-cell px-6 py-3 text-start">
Created at

View File

@@ -1,5 +1,6 @@
<tr{{ attributes }} is="monitor-list-row" id="monitor_{{ monitor.id }}" class="dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-900"
monitor-id="{{ monitor.id }}"
parent-id="{{ monitor.parent.id ?? null }}"
imdb-id="{{ monitor.imdbId }}"
media-title="{{ monitor.title }}"
season="{{ monitor.season }}"
@@ -11,6 +12,7 @@
created-at="{{ monitor.createdAt|date('m/d/Y g:i a') }}"
last-search="{{ monitor.lastSearch|date('m/d/Y g:i a') }}"
downloaded-at="{{null != monitor.downloadedAt ? monitor.downloadedAt|date('m/d/Y g:i a') : '-' }}"
air-date="{{ null != monitor.airDate ? monitor.airDate|date('m/d/Y g:i a') : '-' }}"
>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-stone-800 truncate">
<a href="{{ path('app_search_result', {imdbId: monitor.imdbId, mediaType: monitor.monitorType|as_download_type}) }}"
@@ -26,9 +28,15 @@
<td class="px-6 py-4 whitespace-nowrap text-sm">
{{ monitor|monitor_media_id }}
</td>
{% if null != monitor.parent %}
<td class="hidden md:table-cell px-6 py-4 whitespace-nowrap text-sm">
{{ monitor.searchCount }}
</td>
{% else %}
<td class="hidden md:table-cell px-6 py-4 whitespace-nowrap text-sm">
{{ monitor.children|length }}
</td>
{% endif %}
<td class="hidden md:table-cell px-6 py-4 whitespace-nowrap text-sm">
{{ monitor.createdAt|date('m/d/Y h:i a') }}
</td>