From f4644d40ef117a7985d84b5d997a9021767e19b3 Mon Sep 17 00:00:00 2001 From: Brock H Caldwell Date: Fri, 6 Feb 2026 09:55:19 -0600 Subject: [PATCH] feat: notifies user of bad RD download (failed for copyright, etc.) --- src/Base/Service/Broadcaster.php | 6 +-- .../Action/Command/DownloadMediaCommand.php | 1 + .../Action/Handler/DownloadMediaHandler.php | 43 ++++++++++++++++++- .../Action/Input/DownloadMediaInput.php | 3 ++ .../Framework/Controller/ApiController.php | 8 ++-- .../Repository/DownloadRepository.php | 8 ++++ 6 files changed, 61 insertions(+), 8 deletions(-) diff --git a/src/Base/Service/Broadcaster.php b/src/Base/Service/Broadcaster.php index 2d18a1a..61755b7 100644 --- a/src/Base/Service/Broadcaster.php +++ b/src/Base/Service/Broadcaster.php @@ -24,10 +24,10 @@ readonly class Broadcaster private LoggerInterface $logger, ) {} - public function alert(string $title, string $message, string $type = "success", bool $sendPush = false): void + public function alert(string $title, string $message, string $type = "success", bool $sendPush = false, ?string $mercureAlertTopic = null): void { try { - $userAlertTopic = $this->requestStack->getCurrentRequest()->getSession()->get('mercure_alert_topic'); + $userAlertTopic = $mercureAlertTopic ?? $this->requestStack->getCurrentRequest()->getSession()->get('mercure_alert_topic'); $update = new Update( $userAlertTopic, $this->renderer->render('broadcast/Alert.stream.html.twig', [ @@ -39,7 +39,7 @@ readonly class Broadcaster ); $this->hub->publish($update); } catch (\Throwable $exception) { - // ToDo: look for better handling to get message to end user + $this->logger->error('Unable to publish alert: ' . $exception->getMessage()); } if (true === $sendPush && in_array($this->notificationTransport, ['ntfy'])) { diff --git a/src/Download/Action/Command/DownloadMediaCommand.php b/src/Download/Action/Command/DownloadMediaCommand.php index 6be1482..93f6324 100644 --- a/src/Download/Action/Command/DownloadMediaCommand.php +++ b/src/Download/Action/Command/DownloadMediaCommand.php @@ -17,5 +17,6 @@ class DownloadMediaCommand implements CommandInterface public string $imdbId, public int $userId, public ?int $downloadId = null, + public ?string $mercureAlertTopic = null, ) {} } \ No newline at end of file diff --git a/src/Download/Action/Handler/DownloadMediaHandler.php b/src/Download/Action/Handler/DownloadMediaHandler.php index 5700193..dce6915 100644 --- a/src/Download/Action/Handler/DownloadMediaHandler.php +++ b/src/Download/Action/Handler/DownloadMediaHandler.php @@ -2,9 +2,12 @@ namespace App\Download\Action\Handler; +use App\Base\Enum\MediaType; +use App\Base\Service\Broadcaster; use App\Download\Action\Command\DownloadMediaCommand; use App\Download\Action\Result\DownloadMediaResult; use App\Download\DownloadEvents; +use App\Download\Framework\Entity\Download; use App\Download\Framework\Repository\DownloadRepository; use App\Download\Downloader\DownloaderInterface; use App\EventLog\Action\Command\AddEventLogCommand; @@ -21,8 +24,8 @@ readonly class DownloadMediaHandler implements HandlerInterface public function __construct( private MessageBusInterface $bus, private DownloaderInterface $downloader, - private DownloadRepository $downloadRepository, - private UserRepository $userRepository, + private DownloadRepository $downloadRepository, + private UserRepository $userRepository, private Broadcaster $broadcaster, ) {} public function handle(CommandInterface $command): ResultInterface @@ -49,6 +52,16 @@ readonly class DownloadMediaHandler implements HandlerInterface $download = $this->downloadRepository->find($command->downloadId); } + try { + $this->validateDownloadUrl($download->getUrl()); + } catch (\Throwable $exception) { + $download->setProgress(100); + $download->setStatus('Failed'); + $this->downloadRepository->getEntityManager()->flush(); + $this->sendFailedDownloadAlert($download, $command->mercureAlertTopic, $exception->getMessage()); + return new DownloadMediaResult(400, $exception->getMessage()); + } + try { if ($download->getStatus() !== 'Paused') { $this->downloadRepository->updateStatus($download->getId(), 'In Progress'); @@ -77,4 +90,30 @@ readonly class DownloadMediaHandler implements HandlerInterface )); return new DownloadMediaResult(200, "Success."); } + + public function validateDownloadUrl(string $downloadUrl) + { + $badFileSizes = [ + 2119075, // copyright infringement + ]; + + $badFileLocations = [ + 'https://torrentio.strem.fun/videos/failed_infringement_v2.mp4' => 'Removed for Copyright Infringement.', + ]; + + $headers = get_headers($downloadUrl, true); + if (array_key_exists($headers['Location'], $badFileLocations)) { + throw new \Exception($badFileLocations[$headers['Location']]); + } + } + + private function sendFailedDownloadAlert(Download $download, string $mercureAlertTopic, ?string $message = null): void + { + $this->broadcaster->alert( + title: 'Download Failed', + message: $message ?? "{$download->getTitle()} failed to download.", + type: 'warning', + mercureAlertTopic: $mercureAlertTopic + ); + } } diff --git a/src/Download/Action/Input/DownloadMediaInput.php b/src/Download/Action/Input/DownloadMediaInput.php index cce60a4..7e00c4c 100644 --- a/src/Download/Action/Input/DownloadMediaInput.php +++ b/src/Download/Action/Input/DownloadMediaInput.php @@ -10,6 +10,8 @@ use OneToMany\RichBundle\Contract\InputInterface; /** @implements InputInterface */ class DownloadMediaInput implements InputInterface { + public ?string $mercureAlertTopic = null; + public function __construct( #[SourceRequest('url')] public string $url, @@ -44,6 +46,7 @@ class DownloadMediaInput implements InputInterface $this->imdbId, $this->userId, $this->downloadId, + $this->mercureAlertTopic, ); } } \ No newline at end of file diff --git a/src/Download/Framework/Controller/ApiController.php b/src/Download/Framework/Controller/ApiController.php index c3591fa..ecd38d1 100644 --- a/src/Download/Framework/Controller/ApiController.php +++ b/src/Download/Framework/Controller/ApiController.php @@ -15,6 +15,7 @@ use App\Download\DownloadEvents; use App\Download\Framework\Repository\DownloadRepository; use App\EventLog\Action\Command\AddEventLogCommand; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Routing\Attribute\Route; @@ -22,9 +23,9 @@ use Symfony\Component\Routing\Attribute\Route; class ApiController extends AbstractController { public function __construct( - private DownloadRepository $downloadRepository, - private MessageBusInterface $bus, - private readonly Broadcaster $broadcaster, + private DownloadRepository $downloadRepository, + private MessageBusInterface $bus, + private readonly Broadcaster $broadcaster, private readonly RequestStack $requestStack, ) {} #[Route('/api/download', name: 'api_download', methods: ['POST'])] @@ -42,6 +43,7 @@ class ApiController extends AbstractController ); $input->downloadId = $download->getId(); $input->userId = $this->getUser()->getId(); + $input->mercureAlertTopic = $this->requestStack->getSession()->get('mercure_alert_topic'); $this->bus->dispatch(new AddEventLogCommand( $this->getUser(), diff --git a/src/Download/Framework/Repository/DownloadRepository.php b/src/Download/Framework/Repository/DownloadRepository.php index b796624..e2c6f43 100644 --- a/src/Download/Framework/Repository/DownloadRepository.php +++ b/src/Download/Framework/Repository/DownloadRepository.php @@ -98,6 +98,14 @@ class DownloadRepository extends ServiceEntityRepository return $download; } + public function updateProgress(int $id, int $progress): Download + { + $download = $this->find($id); + $download->setProgress($progress); + $this->getEntityManager()->flush(); + return $download; + } + public function delete(int $id) { $entity = $this->find($id);