fix: broken download, added to queue alert, download list component; feat: monitor list

This commit is contained in:
2025-05-12 11:23:03 -05:00
parent a628d85ef2
commit 888a030680
17 changed files with 205 additions and 107 deletions

View File

@@ -29,7 +29,7 @@ services:
volumes:
- ./:/var/www
- ./var/download:/var/download
command: php ./bin/console messenger:consume async -vv --time-limit=3600
command: php ./bin/console messenger:consume async -vvv --time-limit=3600
scheduler:
build: .

View File

@@ -22,7 +22,7 @@ final class AlertController extends AbstractController
{
$update = new Update(
'alerts',
$this->renderer->render('broadcast/Alert.html.twig', [
$this->renderer->render('Alert.stream.html.twig', [
'alert_id' => 1,
'title' => 'Added to queue',
'message' => 'This is a testy test!',

View File

@@ -7,6 +7,7 @@ use App\Download\Framework\Repository\DownloadRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Routing\Attribute\Route;
@@ -15,6 +16,7 @@ class DownloadController extends AbstractController
public function __construct(
private DownloadRepository $downloadRepository,
private MessageBusInterface $bus,
private readonly HubInterface $hub,
) {}
#[Route('/download', name: 'app_download', methods: ['POST'])]
@@ -30,13 +32,26 @@ class DownloadController extends AbstractController
$input->mediaType,
"",
);
$this->downloadRepository->getEntityManager()->persist($download);
$this->downloadRepository->getEntityManager()->flush();
$input->downloadId = $download->getId();
$input->userId = $this->getUser()->getId();
try {
$this->bus->dispatch($input->toCommand());
} catch (\Throwable $exception) {
return $this->json(['error' => $exception->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR);
}
$this->hub->publish(new Update(
'alerts',
$this->renderView('broadcast/Alert.stream.html.twig', [
'alert_id' => uniqid(),
'title' => 'Success',
'message' => 'Added to Queue',
])
));
return $this->json(['status' => 200, 'message' => 'Added to Queue']);
}
}

View File

@@ -76,7 +76,7 @@ final class TorrentioController extends AbstractController
$this->hub->publish(new Update(
'alerts',
$this->renderer->render('broadcast/Alert.html.twig', [
$this->renderer->render('Alert.stream.html.twig', [
'alert_id' => uniqid(),
'title' => 'Success',
'message' => 'Torrentio cache Cleared.',

View File

@@ -37,6 +37,8 @@ readonly class DownloadMediaHandler implements HandlerInterface
$download = $this->downloadRepository->find($command->downloadId);
}
dump($download);
try {
$this->downloadRepository->updateStatus($download->getId(), 'In Progress');

View File

@@ -39,8 +39,8 @@ class DownloadMediaInput implements InputInterface
$this->filename,
$this->mediaType,
$this->imdbId,
$this->userId,
$this->downloadId,
$this->userId
);
}
}

View File

@@ -33,7 +33,7 @@ class ApiController extends AbstractController
$hub->publish(new Update(
'alerts',
$this->renderer->render('broadcast/Alert.html.twig', [
$this->renderer->render('Alert.stream.html.twig', [
'alert_id' => uniqid(),
'title' => 'Success',
'message' => "New monitor added for {$input->title}",

View File

@@ -1,25 +0,0 @@
<?php
namespace App\Twig\Components;
use App\Download\Framework\Repository\DownloadRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveAction;
use Symfony\UX\LiveComponent\DefaultActionTrait;
#[AsLiveComponent]
final class ActiveDownloadList extends AbstractController
{
use DefaultActionTrait;
public function __construct(
private DownloadRepository $downloadRepository,
) {}
#[LiveAction]
public function getDownloads()
{
return $this->getUser()->getActiveDownloads();
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Twig\Components;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
#[AsTwigComponent]
final class DownloadList extends AbstractController
{
public string $type;
public function getDownloads()
{
if ($this->type === "active") {
return $this->getUser()->queryDownloads('in-progress', 5);
} elseif ($this->type === "complete") {
return $this->getUser()->queryDownloads('complete', 5);
}
return [];
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Twig\Components;
use App\Monitor\Framework\Repository\MonitorRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
#[AsTwigComponent]
final class MonitorList extends AbstractController
{
public function __construct(
private MonitorRepository $monitorRepository,
) {}
public function getUserMonitors()
{
return $this->getUser()->getMonitors();
}
}

View File

@@ -48,7 +48,7 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
* @var Collection<int, Monitor>
*/
#[ORM\OneToMany(targetEntity: Monitor::class, mappedBy: 'user', orphanRemoval: true)]
private Collection $yes;
private Collection $monitors;
/**
* @var Collection<int, Download>
@@ -59,7 +59,7 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
public function __construct()
{
$this->userPreferences = new ArrayCollection();
$this->yes = new ArrayCollection();
$this->monitors = new ArrayCollection();
$this->downloads = new ArrayCollection();
}
@@ -226,27 +226,27 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
/**
* @return Collection<int, Monitor>
*/
public function getYes(): Collection
public function getMonitors(): Collection
{
return $this->yes;
return $this->monitors;
}
public function addYe(Monitor $ye): static
public function addMonitor(Monitor $monitor): static
{
if (!$this->yes->contains($ye)) {
$this->yes->add($ye);
$ye->setUser($this);
if (!$this->monitors->contains($monitor)) {
$this->monitors->add($monitor);
$monitor->setUser($this);
}
return $this;
}
public function removeYe(Monitor $ye): static
public function removeMonitor(Monitor $monitor): static
{
if ($this->yes->removeElement($ye)) {
if ($this->monitors->removeElement($monitor)) {
// set the owning side to null (unless already changed)
if ($ye->getUser() === $this) {
$ye->setUser(null);
if ($monitor->getUser() === $this) {
$monitor->setUser(null);
}
}
@@ -302,4 +302,14 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
return $this;
}
public function queryDownloads(string $type = 'complete', int $limit = 5)
{
if ($type === 'complete') {
return $this->downloads->filter(fn($item) => in_array($item->getStatus(), ['Complete']))->slice(0, $limit);
} elseif ($type === 'in-progress') {
return $this->downloads->filter(fn($item) => in_array($item->getStatus(), ['New', 'In Progress']))->slice(0, $limit);
}
return [];
}
}

View File

@@ -167,7 +167,7 @@ class LdapUserProvider implements UserProviderInterface, PasswordUpgraderInterfa
->setName( $this->getAttributeValue($entry, $this->displayNameAttribute)[0] ?? null)
->setEmail($this->getAttributeValue($entry, $this->emailAttribute)[0] ?? null)
->setUsername($this->getAttributeValue($entry, $this->usernameAttribute) ?? null);
$this->userRepository->getEntityManager()->persist($dbUser);
$this->userRepository->getEntityManager()->flush();

View File

@@ -1,21 +1,28 @@
{# Learn how to use Turbo Streams: https://github.com/symfony/ux-turbo#broadcast-doctrine-entities-update #}
{% block create %}
<turbo-stream action="remove" target="active_downloads_no_downloads">
</turbo-stream>
<turbo-stream action="append" target="active_downloads">
<template>
<tr id="ad_download_{{ entity.id }}">
<tr id="ad_download_{{ entity.id }}">
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-stone-800 min-w-[45ch] max-w-[45ch] truncate">
{{ entity.title }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-end text-gray-800 dark:text-gray-50">
<span class="p-1.5 bg-purple-600 rounded-full">
<span class="w-4 inline-block text-center text-gray-50">{{ entity.progress }}</span>
</span>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-800 dark:text-gray-50">
{% if entity.progress < 100 %}
<span class="p-1.5 bg-purple-600 rounded-full">
<span class="w-4 inline-block text-center text-gray-50">{{ entity.progress }}</span>
</span>
{% else %}
<span class="p-1.5 bg-green-600 rounded-full">
<span class="inline-block text-center text-gray-50">Complete</span>
</span>
{% endif %}
</td>
</tr>
</template>
</turbo-stream>
<twig:Alert title="Success" message="{{ entity.title }} has been added to the Download queue" alert_id="{{ entity.id }}" data-controller="alert" />
{% endblock %}
{% block update %}
@@ -33,6 +40,9 @@
</template>
</turbo-stream>
{% else %}
<turbo-stream action="remove" target="complete_downloads_no_downloads">
</turbo-stream>
<turbo-stream action="remove" target="ad_download_{{ id }}">
</turbo-stream>
@@ -42,16 +52,16 @@
</template>
</turbo-stream>
<turbo-stream action="prepend" target="recent_downloads">
<turbo-stream action="prepend" target="complete_downloads">
<template>
<tr id="recent_download_{{ entity.id }}">
<tr id="ad_download_{{ entity.id }}">
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-stone-800 min-w-[45ch] max-w-[45ch] truncate">
{{ entity.title }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-end text-gray-800 dark:text-gray-50">
<span class="p-1.5 bg-purple-600 rounded-full">
<span class="w-4 inline-block text-center text-gray-50">{{ entity.progress }}</span>
</span>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-800 dark:text-gray-50">
<span class="p-1.5 bg-green-600 rounded-full">
<span class="inline-block text-center text-gray-50">Complete</span>
</span>
</td>
</tr>
</template>

View File

@@ -1,9 +1,10 @@
<div{{ attributes }} class="min-w-48">
<table id="active_downloads" class="divide-y divide-gray-200 dark:divide-gray-50 dark:bg-gray-50 table-fixed" {{ turbo_stream_listen('App\\Download\\Framework\\Entity\\Download') }}>
{% set table_body_id = (type == "complete") ? "complete_downloads" : "active_downloads" %}
<table id="downloads" class="divide-y divide-gray-200 bg-gray-50 overflow-hidden rounded-lg table-fixed" {{ turbo_stream_listen('App\\Download\\Framework\\Entity\\Download') }}>
<thead>
<tr class="dark:bg-gray-50">
<tr class="bg-orange-500 bg-filter bg-blur-lg bg-opacity-80 text-gray-950">
<th scope="col"
class="px-6 py-3 text-start text-xs font-medium text-stone-500 uppercase dark:text-stone-800 min-w-[55ch] max-w-[55ch] truncate">
class="px-6 py-3 text-start text-xs font-medium text-stone-500 uppercase dark:text-stone-800 min-w-[45ch] max-w-[45ch] truncate">
Title
</th>
<th scope="col"
@@ -12,24 +13,30 @@
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 dark:divide-gray-50">
{% if this.getDownloads()|length > 0 %}
{% for download in this.getDownloads() %}
<tbody id="{{ table_body_id }}" class="divide-y divide-gray-200 dark:divide-gray-50">
{% if this.downloads|length > 0 %}
{% for download in this.downloads %}
<tr id="ad_download_{{ download.id }}">
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-stone-800 min-w-[45ch] max-w-[45ch] truncate">
{{ download.title }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-end text-gray-800 dark:text-gray-50">
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-800 dark:text-gray-50">
{% if download.progress < 100 %}
<span class="p-1.5 bg-purple-600 rounded-full">
<span class="w-4 inline-block text-center text-gray-50">{{ download.progress }}</span>
</span>
{% else %}
<span class="p-1.5 bg-green-600 rounded-full">
<span class="inline-block text-center text-gray-50">Complete</span>
</span>
{% endif %}
</td>
</tr>
{% endfor %}
{% else %}
<tr>
<tr id="{{ table_body_id }}_no_downloads">
<td class="px-6 py-4 whitespace-nowrap text-xs uppercase text-center col-span-2 font-medium text-gray-800 dark:text-stone-800" colspan="2">
No active downloads
No downloads
</td>
</tr>
{% endif %}

View File

@@ -0,0 +1,71 @@
<div{{ attributes }} class="min-w-48">
<p class="text-white mb-1">The items you're currently monitoring to automatically download.</p>
<table id="downloads" class="divide-y divide-gray-200 bg-gray-50 overflow-hidden rounded-lg table-fixed" {{ turbo_stream_listen('App\\Download\\Framework\\Entity\\Download') }}>
<thead>
<tr class="bg-orange-500 bg-filter bg-blur-lg bg-opacity-80 text-gray-950">
<th scope="col"
class="px-6 py-3 text-start text-xs font-medium uppercase min-w-[45ch] max-w-[45ch] truncate">
Title
</th>
<th scope="col"
class="px-6 py-3 text-start text-xs font-medium uppercase">
Search Count
</th>
<th scope="col"
class="px-6 py-3 text-start text-xs font-medium uppercase">
Created at
</th>
<th scope="col"
class="px-6 py-3 text-start text-xs font-medium uppercase">
Last Search Date
</th>
<th scope="col"
class="px-6 py-3 text-start text-xs font-medium uppercase">
Status
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 dark:divide-gray-50">
{% if this.userMonitors()|length > 0 %}
{% for monitor in this.userMonitors() %}
<tr id="monitor_{{ monitor.id }}">
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-stone-800 min-w-[45ch] max-w-[45ch] truncate">
{{ monitor.title }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-800">
{{ monitor.searchCount }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-800">
{{ monitor.createdAt|date('m/d/Y h:i a') }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-800">
{{ monitor.lastSearch|date('m/d/Y h:i a') }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-800">
{% if monitor.status == "New" %}
<span class="p-1.5 bg-orange-500 rounded-full">
<span class="w-4 inline-block text-center text-gray-50">{{ monitor.status }}</span>
</span>
{% elseif monitor.status == "In Progress" or monitor.status == "Active" %}
<span class="p-1.5 bg-purple-600 rounded-full">
<span class="inline-block text-center text-gray-50">{{ monitor.status }}</span>
</span>
{% else %}
<span class="p-1.5 bg-green-600 rounded-full">
<span class="inline-block text-center text-gray-50">{{ monitor.status }}</span>
</span>
{% endif %}
</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td class="px-6 py-4 whitespace-nowrap text-xs uppercase text-center col-span-2 font-medium text-gray-800 dark:text-stone-800" colspan="2">
No monitors
</td>
</tr>
{% endif %}
</tbody>
</table>
</div>

View File

@@ -7,51 +7,16 @@
<h2 class="mb-2 text-3xl font-bold text-gray-50">Dashboard</h2>
<div class="flex flex-row gap-4">
<twig:Card title="Active Downloads" class="w-full">
<twig:ActiveDownloadList />
<twig:DownloadList :type="'active'" />
</twig:Card>
<twig:Card title="Recent Downloads" class="w-full">
<table id="recent_downloads" class="divide-y divide-gray-200 dark:divide-gray-50 dark:bg-gray-50 table-fixed">
<thead>
<tr class="dark:bg-gray-50">
<th scope="col"
class="px-6 py-3 text-start text-xs font-medium text-stone-500 uppercase dark:text-stone-800 rounded-tl-md">
Title
</th>
<th scope="col"
class="px-6 py-3 text-start text-xs font-medium text-gray-500 uppercase dark:text-stone-800">
Status
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 dark:divide-gray-50">
{% if recent_downloads|length > 0 %}
{% for download in recent_downloads %}
<tr>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-stone-800">
{{ download.title }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-end text-gray-800 dark:text-gray-50">
<span class="p-1 bg-green-600 rounded-lg">
<span class="text-gray-50">Complete</span>
</span>
</td>
</tr>
{% endfor %}
<tr class="bg-blue-400">
<td class="px-6 py-3 whitespace-nowrap text-xs uppercase text-center col-span-2 font-medium text-rose-400 dark:text-stone-800" colspan="2">
<a href="#">View all downloads</a>
</td>
</tr>
{% else %}
<tr>
<td class="px-6 py-4 whitespace-nowrap text-xs uppercase text-center col-span-2 font-medium text-gray-800 dark:text-stone-800" colspan="2">
No recent downloads
</td>
</tr>
{% endif %}
</tbody>
</table>
<twig:DownloadList :type="'complete'" />
</twig:Card>
</div>
<div class="flex flex-row gap-4">
<twig:Card title="Monitors" class="w-full">
<twig:MonitorList />
</twig:Card>
</div>
<div class="flex flex-col gap-4">