Compare commits
8 Commits
8cc81fea19
...
v0.18.3
| Author | SHA1 | Date | |
|---|---|---|---|
| 9b3506ab17 | |||
| 6e0eed8b4e | |||
| 38a5baa17e | |||
| 1d573c09e7 | |||
| 7989e2abd2 | |||
| df6c68aa46 | |||
| 6cd9a9b18e | |||
| b95e8f3794 |
@@ -23,6 +23,7 @@ services:
|
||||
- mercure_config:/config
|
||||
tty: true
|
||||
environment:
|
||||
TZ: America/Chicago
|
||||
MERCURE_PUBLISHER_JWT_KEY: '!ChangeThisMercureHubJWTSecretKey!'
|
||||
MERCURE_SUBSCRIBER_JWT_KEY: '!ChangeThisMercureHubJWTSecretKey!'
|
||||
depends_on:
|
||||
@@ -37,6 +38,8 @@ services:
|
||||
- $PWD:/app
|
||||
- $PWD/var/download:/var/download
|
||||
tty: true
|
||||
environment:
|
||||
TZ: America/Chicago
|
||||
command: php /app/bin/console messenger:consume async -vv --time-limit=3600 --limit=10
|
||||
|
||||
|
||||
@@ -45,6 +48,8 @@ services:
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- $PWD:/app
|
||||
environment:
|
||||
TZ: America/Chicago
|
||||
command: php /app/bin/console messenger:consume scheduler_monitor -vv
|
||||
tty: true
|
||||
|
||||
@@ -55,6 +60,8 @@ services:
|
||||
- redis_data:/data
|
||||
command: redis-server --maxmemory 512MB
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
TZ: America/Chicago
|
||||
|
||||
|
||||
database:
|
||||
@@ -64,6 +71,7 @@ services:
|
||||
volumes:
|
||||
- mysql:/var/lib/mysql
|
||||
environment:
|
||||
TZ: America/Chicago
|
||||
MYSQL_DATABASE: app
|
||||
MYSQL_USERNAME: app
|
||||
MYSQL_PASSWORD: password
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
twig:
|
||||
file_name_pattern: '*.twig'
|
||||
date:
|
||||
timezone: '%env(default:app.default.timezone:TZ)%'
|
||||
|
||||
when@test:
|
||||
twig:
|
||||
|
||||
@@ -21,6 +21,9 @@ parameters:
|
||||
app.cache.adapter.default: 'filesystem'
|
||||
app.cache.redis.host.default: 'redis://redis'
|
||||
|
||||
# Various configs
|
||||
app.default.timezone: 'America/Chicago'
|
||||
|
||||
services:
|
||||
# default configuration for services in *this* file
|
||||
_defaults:
|
||||
|
||||
@@ -6,6 +6,7 @@ services:
|
||||
environment:
|
||||
MERCURE_PUBLISHER_JWT_KEY: '!ChangeThisMercureHubJWTSecretKey!'
|
||||
MERCURE_SUBSCRIBER_JWT_KEY: '!ChangeThisMercureHubJWTSecretKey!'
|
||||
tty: true
|
||||
deploy:
|
||||
replicas: 2
|
||||
volumes:
|
||||
|
||||
@@ -39,6 +39,12 @@ readonly class MonitorTvSeasonHandler implements HandlerInterface
|
||||
$downloadedEpisodes = $this->mediaFiles
|
||||
->getEpisodes($monitor->getTitle())
|
||||
->map(fn($episode) => (object) (new PTN())->parse($episode))
|
||||
->filter(fn ($episode) =>
|
||||
property_exists($episode, 'episode')
|
||||
&& property_exists($episode, 'season')
|
||||
&& null !== $episode->episode
|
||||
&& null !== $episode->season
|
||||
)
|
||||
->rekey(fn($episode) => $episode->episode);
|
||||
$this->logger->info('> [MonitorTvSeasonHandler] Found ' . count($downloadedEpisodes) . ' downloaded episodes for title: ' . $monitor->getTitle());
|
||||
|
||||
@@ -49,51 +55,48 @@ readonly class MonitorTvSeasonHandler implements HandlerInterface
|
||||
$this->logger->info('> [MonitorTvSeasonHandler] Found ' . count($episodesInSeason) . ' episodes in season ' . $monitor->getSeason() . ' for title: ' . $monitor->getTitle());
|
||||
|
||||
if ($downloadedEpisodes->count() !== $episodesInSeason->count()) {
|
||||
// Since $monitor has children monitors, set the status
|
||||
// to Active, so it will be re-executed.
|
||||
$monitor->setStatus('Active');
|
||||
|
||||
// Dispatch Episode commands for each missing Episode
|
||||
foreach ($episodesInSeason as $episode) {
|
||||
$monitorCheck = $this->monitorRepository->findOneBy([
|
||||
'imdbId' => $monitor->getImdbId(),
|
||||
'title' => $monitor->getTitle(),
|
||||
'monitorType' => 'tvepisode',
|
||||
'season' => $monitor->getSeason(),
|
||||
'episode' => $episode['episode_number'],
|
||||
'status' => ['New', 'Active', 'In Progress']
|
||||
]);
|
||||
|
||||
$this->logger->info('> [MonitorTvSeasonHandler] Monitor exists for season ' . $monitor->getSeason() . ' episode ' . $episode['episode_number'] . ' for title: ' . $monitor->getTitle() . ' ? ' . (null !== $monitorCheck ? 'YES' : 'NO'));
|
||||
|
||||
if (!array_key_exists($episode['episode_number'], $downloadedEpisodes->toArray())
|
||||
&& null === $monitorCheck
|
||||
) {
|
||||
$episodeMonitor = (new Monitor())
|
||||
->setParent($monitor)
|
||||
->setUser($monitor->getUser())
|
||||
->setTmdbId($monitor->getTmdbId())
|
||||
->setImdbId($monitor->getImdbId())
|
||||
->setTitle($monitor->getTitle())
|
||||
->setMonitorType('tvepisode')
|
||||
->setSeason($monitor->getSeason())
|
||||
->setEpisode($episode['episode_number'])
|
||||
->setCreatedAt(new DateTimeImmutable())
|
||||
->setSearchCount(0)
|
||||
->setStatus('New');
|
||||
|
||||
$this->monitorRepository->getEntityManager()->persist($episodeMonitor);
|
||||
$this->monitorRepository->getEntityManager()->flush();
|
||||
|
||||
$command = new MonitorTvEpisodeCommand($episodeMonitor->getId());
|
||||
$this->monitorTvEpisodeHandler->handle($command);
|
||||
$this->logger->info('> [MonitorTvSeasonHandler] Dispatching MonitorTvEpisodeCommand for season ' . $episodeMonitor->getSeason() . ' episode ' . $episodeMonitor->getEpisode() . ' for title: ' . $monitor->getTitle());
|
||||
// Check if the episode is already downloaded
|
||||
$episodeExists = $this->episodeExists($episode, $downloadedEpisodes);
|
||||
$this->logger->info('> [MonitorTvSeasonHandler] Episode exists for season ' . $episode['season_number'] . ' episode ' . $episode['episode_number'] . ' for title: ' . $monitor->getTitle() . ' ? ' . (true === $episodeExists ? 'YES' : 'NO'));
|
||||
if (true === $episodeExists) {
|
||||
$this->logger->info('> [MonitorTvSeasonHandler] Episode exists for title: ' . $monitor->getTitle() . ', skipping');
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for existing monitors
|
||||
$monitorExists = $this->monitorExists($monitor, $episode);
|
||||
$this->logger->info('> [MonitorTvSeasonHandler] Monitor exists for season ' . $episode['season_number'] . ' episode ' . $episode['episode_number'] . ' for title: ' . $monitor->getTitle() . ' ? ' . (true === $monitorExists ? 'YES' : 'NO'));
|
||||
if (true === $monitorExists) {
|
||||
$this->logger->info('> [MonitorTvSeasonHandler] Monitor exists for title: ' . $monitor->getTitle() . ', skipping');
|
||||
continue;
|
||||
}
|
||||
|
||||
$episodeMonitor = (new Monitor())
|
||||
->setParent($monitor)
|
||||
->setUser($monitor->getUser())
|
||||
->setTmdbId($monitor->getTmdbId())
|
||||
->setImdbId($monitor->getImdbId())
|
||||
->setTitle($monitor->getTitle())
|
||||
->setMonitorType('tvepisode')
|
||||
->setSeason($monitor->getSeason())
|
||||
->setEpisode($episode['episode_number'])
|
||||
->setCreatedAt(new DateTimeImmutable())
|
||||
->setSearchCount(0)
|
||||
->setStatus('New');
|
||||
|
||||
$this->monitorRepository->getEntityManager()->persist($episodeMonitor);
|
||||
$this->monitorRepository->getEntityManager()->flush();
|
||||
|
||||
$command = new MonitorTvEpisodeCommand($episodeMonitor->getId());
|
||||
$this->monitorTvEpisodeHandler->handle($command);
|
||||
$this->logger->info('> [MonitorTvSeasonHandler] Dispatching MonitorTvEpisodeCommand for season ' . $episodeMonitor->getSeason() . ' episode ' . $episodeMonitor->getEpisode() . ' for title: ' . $monitor->getTitle());
|
||||
}
|
||||
} else {
|
||||
$monitor->setStatus('Complete');
|
||||
}
|
||||
|
||||
// Set the status to Active, so it will be re-executed.
|
||||
$monitor->setStatus('Active');
|
||||
$monitor->setLastSearch(new DateTimeImmutable());
|
||||
$monitor->setSearchCount($monitor->getSearchCount() + 1);
|
||||
|
||||
@@ -106,4 +109,24 @@ readonly class MonitorTvSeasonHandler implements HandlerInterface
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
private function episodeExists(array $episodeInShow, Map $downloadedEpisodes): bool
|
||||
{
|
||||
return $downloadedEpisodes->filter(
|
||||
fn (object $episode) => $episode->episode === $episodeInShow['episode_number']
|
||||
&& $episode->season === $episodeInShow['season_number']
|
||||
)->count() > 0;
|
||||
}
|
||||
|
||||
private function monitorExists(Monitor $monitor, array $episode): bool
|
||||
{
|
||||
return $this->monitorRepository->findOneBy([
|
||||
'imdbId' => $monitor->getImdbId(),
|
||||
'title' => $monitor->getTitle(),
|
||||
'monitorType' => 'tvepisode',
|
||||
'season' => $episode['season_number'],
|
||||
'episode' => $episode['episode_number'],
|
||||
'status' => ['New', 'Active', 'In Progress']
|
||||
]) !== null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,61 +38,68 @@ readonly class MonitorTvShowHandler implements HandlerInterface
|
||||
// Check current episodes
|
||||
$downloadedEpisodes = $this->mediaFiles
|
||||
->getEpisodes($monitor->getTitle())
|
||||
->map(fn($episode) => (object) (new PTN())->parse($episode));
|
||||
->map(fn($episode) => (object) (new PTN())->parse($episode))
|
||||
->filter(fn ($episode) =>
|
||||
property_exists($episode, 'episode')
|
||||
&& property_exists($episode, 'season')
|
||||
&& null !== $episode->episode
|
||||
&& null !== $episode->season
|
||||
)
|
||||
;
|
||||
$this->logger->info('> [MonitorTvShowHandler] Found ' . count($downloadedEpisodes) . ' downloaded episodes for title: ' . $monitor->getTitle());
|
||||
|
||||
// Compare against list from TMDB
|
||||
$episodesInShow = Map::from(
|
||||
$this->tmdb->tvDetails($monitor->getTmdbId())->episodes
|
||||
)->flat(1);
|
||||
|
||||
$this->logger->info('> [MonitorTvShowHandler] Found ' . count($episodesInShow) . ' episodes for title: ' . $monitor->getTitle());
|
||||
|
||||
if ($downloadedEpisodes->count() !== $episodesInShow->count()) {
|
||||
// Since $monitor has children monitors, set the status
|
||||
// to Active, so it will be re-executed.
|
||||
$monitor->setStatus('Active');
|
||||
|
||||
// Dispatch Episode commands for each missing Episode
|
||||
foreach ($episodesInShow as $episode) {
|
||||
$monitorCheck = $this->monitorRepository->findOneBy([
|
||||
'imdbId' => $monitor->getImdbId(),
|
||||
'title' => $monitor->getTitle(),
|
||||
'monitorType' => 'tvepisode',
|
||||
'season' => $monitor->getSeason(),
|
||||
'episode' => $episode['episode_number'],
|
||||
'status' => ['New', 'Active', 'In Progress']
|
||||
]);
|
||||
|
||||
$this->logger->info('> [MonitorTvShowHandler] Monitor exists for season ' . $monitor->getSeason() . ' episode ' . $episode['episode_number'] . ' for title: ' . $monitor->getTitle() . ' ? ' . (null !== $monitorCheck ? 'YES' : 'NO'));
|
||||
|
||||
if (!array_key_exists($episode['episode_number'], $downloadedEpisodes->toArray())
|
||||
&& null === $monitorCheck
|
||||
) {
|
||||
$episodeMonitor = (new Monitor())
|
||||
->setParent($monitor)
|
||||
->setUser($monitor->getUser())
|
||||
->setTmdbId($monitor->getTmdbId())
|
||||
->setImdbId($monitor->getImdbId())
|
||||
->setTitle($monitor->getTitle())
|
||||
->setMonitorType('tvepisode')
|
||||
->setSeason($monitor->getSeason())
|
||||
->setEpisode($episode['episode_number'])
|
||||
->setCreatedAt(new DateTimeImmutable())
|
||||
->setSearchCount(0)
|
||||
->setStatus('New');
|
||||
|
||||
$this->monitorRepository->getEntityManager()->persist($episodeMonitor);
|
||||
$this->monitorRepository->getEntityManager()->flush();
|
||||
|
||||
$command = new MonitorTvEpisodeCommand($episodeMonitor->getId());
|
||||
$this->monitorTvEpisodeHandler->handle($command);
|
||||
$this->logger->info('> [MonitorTvShowHandler] Dispatching MonitorTvEpisodeCommand for season ' . $episodeMonitor->getSeason() . ' episode ' . $episodeMonitor->getEpisode() . ' for title: ' . $monitor->getTitle());
|
||||
// Check if the episode is already downloaded
|
||||
$episodeExists = $this->episodeExists($episode, $downloadedEpisodes);
|
||||
$this->logger->info('> [MonitorTvShowHandler] Episode exists for season ' . $episode['season_number'] . ' episode ' . $episode['episode_number'] . ' for title: ' . $monitor->getTitle() . ' ? ' . (true === $episodeExists ? 'YES' : 'NO'));
|
||||
if (true === $episodeExists) {
|
||||
$this->logger->info('> [MonitorTvShowHandler] Episode exists for title: ' . $monitor->getTitle() . ', skipping');
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for existing monitors
|
||||
$monitorExists = $this->monitorExists($monitor, $episode);
|
||||
$this->logger->info('> [MonitorTvShowHandler] Monitor exists for season ' . $episode['season_number'] . ' episode ' . $episode['episode_number'] . ' for title: ' . $monitor->getTitle() . ' ? ' . (true === $monitorExists ? 'YES' : 'NO'));
|
||||
if (true === $monitorExists) {
|
||||
$this->logger->info('> [MonitorTvShowHandler] Monitor exists for title: ' . $monitor->getTitle() . ', skipping');
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create the monitor
|
||||
$episodeMonitor = (new Monitor())
|
||||
->setParent($monitor)
|
||||
->setUser($monitor->getUser())
|
||||
->setTmdbId($monitor->getTmdbId())
|
||||
->setImdbId($monitor->getImdbId())
|
||||
->setTitle($monitor->getTitle())
|
||||
->setMonitorType('tvepisode')
|
||||
->setSeason($episode['season_number'])
|
||||
->setEpisode($episode['episode_number'])
|
||||
->setCreatedAt(new DateTimeImmutable())
|
||||
->setSearchCount(0)
|
||||
->setStatus('New');
|
||||
|
||||
$this->monitorRepository->getEntityManager()->persist($episodeMonitor);
|
||||
$this->monitorRepository->getEntityManager()->flush();
|
||||
|
||||
// Immediately run the monitor
|
||||
$command = new MonitorTvEpisodeCommand($episodeMonitor->getId());
|
||||
$this->monitorTvEpisodeHandler->handle($command);
|
||||
$this->logger->info('> [MonitorTvShowHandler] Dispatching MonitorTvEpisodeCommand for season ' . $episodeMonitor->getSeason() . ' episode ' . $episodeMonitor->getEpisode() . ' for title: ' . $monitor->getTitle());
|
||||
}
|
||||
} else {
|
||||
$monitor->setStatus('Complete');
|
||||
}
|
||||
|
||||
// Set the status to Active, so it will be re-executed.
|
||||
$monitor->setStatus('Active');
|
||||
$monitor->setLastSearch(new DateTimeImmutable());
|
||||
$monitor->setSearchCount($monitor->getSearchCount() + 1);
|
||||
$this->entityManager->flush();
|
||||
@@ -104,4 +111,24 @@ readonly class MonitorTvShowHandler implements HandlerInterface
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
private function episodeExists(array $episodeInShow, Map $downloadedEpisodes): bool
|
||||
{
|
||||
return $downloadedEpisodes->filter(
|
||||
fn (object $episode) => $episode->episode === $episodeInShow['episode_number']
|
||||
&& $episode->season === $episodeInShow['season_number']
|
||||
)->count() > 0;
|
||||
}
|
||||
|
||||
private function monitorExists(Monitor $monitor, array $episode): bool
|
||||
{
|
||||
return $this->monitorRepository->findOneBy([
|
||||
'imdbId' => $monitor->getImdbId(),
|
||||
'title' => $monitor->getTitle(),
|
||||
'monitorType' => 'tvepisode',
|
||||
'season' => $episode['season_number'],
|
||||
'episode' => $episode['episode_number'],
|
||||
'status' => ['New', 'Active', 'In Progress']
|
||||
]) !== null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Messenger\MessageBusInterface;
|
||||
use Symfony\Component\Scheduler\Attribute\AsCronTask;
|
||||
|
||||
#[AsCronTask('* * * * *', schedule: 'monitor')]
|
||||
#[AsCronTask('0 * * * *', schedule: 'monitor')]
|
||||
class MonitorDispatcher
|
||||
{
|
||||
public function __construct(
|
||||
|
||||
@@ -152,7 +152,7 @@ class MediaFiles
|
||||
foreach ($existingEpisodes as $episode) {
|
||||
$ptn = (object) (new PTN())->parse($episode->getFilename());
|
||||
|
||||
if (!property_exists($episode, 'season') || !property_exists($episode, 'episode')) {
|
||||
if (!property_exists($ptn, 'season') || !property_exists($ptn, 'episode')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,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)')
|
||||
->setParameter('statuses', ['New', 'In Progress'])
|
||||
->setParameter('statuses', ['New', 'In Progress', 'Active'])
|
||||
->setParameter('term', '%'.$this->term.'%')
|
||||
->orderBy('m.id', 'DESC')
|
||||
->getQuery()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<li {{ attributes }} id="alert_{{ alert_id }}" class="
|
||||
text-white bg-green-950 text-sm min-w-[250px]
|
||||
hover:bg-green-900 border border-green-500 px-4 py-3
|
||||
rounded-md z-40"
|
||||
rounded-md"
|
||||
role="alert"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<tr{{ attributes }} id="ad_download_{{ download.id }}">
|
||||
<tr{{ attributes }} class="hover:bg-gray-200" 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">
|
||||
<a href="{{ path('app_search_result', {imdbId: download.imdbId, mediaType: download.mediaType}) }}"
|
||||
class="mr-1 hover:underline rounded-md"
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div {{ turbo_stream_listen(app.session.get('mercure_alert_topic')) }} class="absolute top-10 right-10">
|
||||
<div >
|
||||
<div {{ turbo_stream_listen(app.session.get('mercure_alert_topic')) }} class="fixed z-40 top-10 right-10">
|
||||
<div class="z-40">
|
||||
<ul id="alert_list">
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<tr{{ attributes }} id="monitor_{{ monitor.id }}">
|
||||
<tr{{ attributes }} id="monitor_{{ monitor.id }}" class="hover:bg-gray-200">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-stone-800 truncate">
|
||||
{{ monitor.title }}
|
||||
</td>
|
||||
|
||||
Reference in New Issue
Block a user