From 62ad522286bc8a2ab1c094613ec1ab7866658972 Mon Sep 17 00:00:00 2001 From: ghost Date: Mon, 9 Oct 2023 00:30:13 +0300 Subject: [PATCH] implement torrent/magnet download feature --- src/Controller/TorrentController.php | 302 +++++++++++--- src/Entity/TorrentDownloadFile.php | 72 ++++ src/Entity/TorrentDownloadMagnet.php | 72 ++++ .../TorrentDownloadFileRepository.php | 53 +++ .../TorrentDownloadMagnetRepository.php | 53 +++ src/Service/TorrentService.php | 383 +++++++++++------- templates/default/torrent/info.html.twig | 40 +- 7 files changed, 754 insertions(+), 221 deletions(-) create mode 100644 src/Entity/TorrentDownloadFile.php create mode 100644 src/Entity/TorrentDownloadMagnet.php create mode 100644 src/Repository/TorrentDownloadFileRepository.php create mode 100644 src/Repository/TorrentDownloadMagnetRepository.php diff --git a/src/Controller/TorrentController.php b/src/Controller/TorrentController.php index 2aba97a..db57bf2 100644 --- a/src/Controller/TorrentController.php +++ b/src/Controller/TorrentController.php @@ -59,15 +59,38 @@ class TorrentController extends AbstractController 'added' => $torrent->getAdded(), 'locales' => $torrentService->findLastTorrentLocales($torrent->getId()), 'sensitive' => $torrentService->findLastTorrentSensitive($torrent->getId())->isValue(), + 'download' => + [ + 'file' => + [ + 'exist' => (bool) $torrentService->findTorrentDownloadFile( + $torrent->getId(), + $user->getId() + ), + 'total' => $torrentService->findTorrentDownloadFilesTotalByTorrentId( + $torrent->getId() + ) + ], + 'magnet' => + [ + 'exist' => (bool) $torrentService->findTorrentDownloadMagnet( + $torrent->getId(), + $user->getId() + ), + 'total' => $torrentService->findTorrentDownloadMagnetsTotalByTorrentId( + $torrent->getId() + ) + ] + ], 'bookmark' => [ - 'active' => (bool) $torrentService->findTorrentBookmark( + 'exist' => (bool) $torrentService->findTorrentBookmark( $torrent->getId(), $user->getId() ), - 'total' => $torrentService->findTorrentBookmarksTotalByTorrentId( + 'total' => $torrentService->findTorrentBookmarksTotalByTorrentId( $torrent->getId() - ), + ) ], 'pages' => [] ], @@ -235,62 +258,6 @@ class TorrentController extends AbstractController ); } - // Torrent bookmark - #[Route( - '/{_locale}/torrent/{torrentId}/bookmark/toggle', - name: 'torrent_bookmark_toggle', - requirements: - [ - 'torrentId' => '\d+', - ], - methods: - [ - 'GET' - ] - )] - public function toggleBookmark( - Request $request, - TranslatorInterface $translator, - UserService $userService, - TorrentService $torrentService - ): Response - { - // Init user - $user = $userService->init( - $request->getClientIp() - ); - - if (!$user->isStatus()) - { - // @TODO - throw new \Exception( - $translator->trans('Access denied') - ); - } - - // Init torrent - if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) - { - throw $this->createNotFoundException(); - } - - // Update - $torrentService->toggleTorrentBookmark( - $torrent->getId(), - $user->getId(), - time() - ); - - // Redirect to info page created - return $this->redirectToRoute( - 'torrent_info', - [ - '_locale' => $request->get('_locale'), - 'torrentId' => $torrent->getId() - ] - ); - } - // Torrent locales #[Route( '/{_locale}/torrent/{torrentId}/edit/locales/{torrentLocalesId}', @@ -888,4 +855,221 @@ class TorrentController extends AbstractController ] ); } + + // Torrent bookmark + #[Route( + '/{_locale}/torrent/{torrentId}/bookmark/toggle', + name: 'torrent_bookmark_toggle', + requirements: + [ + 'torrentId' => '\d+', + ], + methods: + [ + 'GET' + ] + )] + public function toggleBookmark( + Request $request, + TranslatorInterface $translator, + UserService $userService, + TorrentService $torrentService + ): Response + { + // Init user + $user = $userService->init( + $request->getClientIp() + ); + + if (!$user->isStatus()) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Init torrent + if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) + { + throw $this->createNotFoundException(); + } + + // Update + $torrentService->toggleTorrentBookmark( + $torrent->getId(), + $user->getId(), + time() + ); + + // Redirect to info page created + return $this->redirectToRoute( + 'torrent_info', + [ + '_locale' => $request->get('_locale'), + 'torrentId' => $torrent->getId() + ] + ); + } + + // Torrent download file + #[Route( + '/{_locale}/torrent/{torrentId}/download/file', + name: 'torrent_download_file', + requirements: + [ + 'torrentId' => '\d+', + ], + methods: + [ + 'GET' + ] + )] + public function downloadFile( + Request $request, + TranslatorInterface $translator, + UserService $userService, + TorrentService $torrentService + ): Response + { + // Init user + $user = $userService->init( + $request->getClientIp() + ); + + if (!$user->isStatus()) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Init torrent + if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) + { + throw $this->createNotFoundException(); + } + + if (!$file = $torrentService->readTorrentFileByTorrentId($torrent->getId())) + { + // @TODO + throw new \Exception( + $translator->trans('File not found') + ); + } + + // Register download + $torrentService->registerTorrentDownloadFile( + $torrent->getId(), + $user->getId(), + time() + ); + + // Filter trackers + $file->setAnnounceList( + [ + explode('|', $this->getParameter('app.trackers')) + ] + ); + + $data = $file->dumpToString(); + + // Set headers + $response = new Response(); + + $response->headers->set( + 'Content-type', + 'application/x-bittorrent' + ); + + $response->headers->set( + 'Content-length', + strlen($data) + ); + + $response->headers->set( + 'Content-Disposition', + sprintf( + 'attachment; filename="%s.%s.%s.torrent";', + $this->getParameter('app.name'), + $torrent->getId(), + mb_strtolower( + $file->getName() + ) + ) + ); + + $response->sendHeaders(); + + // Return file content + return $response->setContent($data); + } + + // Torrent download magnet + #[Route( + '/{_locale}/torrent/{torrentId}/download/magnet', + name: 'torrent_download_magnet', + requirements: + [ + 'torrentId' => '\d+', + ], + methods: + [ + 'GET' + ] + )] + public function getMagnet( + Request $request, + TranslatorInterface $translator, + UserService $userService, + TorrentService $torrentService + ): Response + { + // Init user + $user = $userService->init( + $request->getClientIp() + ); + + if (!$user->isStatus()) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Init torrent + if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) + { + throw $this->createNotFoundException(); + } + + if (!$file = $torrentService->readTorrentFileByTorrentId($torrent->getId())) + { + // @TODO + throw new \Exception( + $translator->trans('File not found') + ); + } + + // Register download + $torrentService->registerTorrentDownloadMagnet( + $torrent->getId(), + $user->getId(), + time() + ); + + // Filter trackers + $file->setAnnounceList( + [ + explode('|', $this->getParameter('app.trackers')) + ] + ); + + // Return magnet link + return $this->redirect( + $file->getMagnetLink() + ); + } } diff --git a/src/Entity/TorrentDownloadFile.php b/src/Entity/TorrentDownloadFile.php new file mode 100644 index 0000000..f05b416 --- /dev/null +++ b/src/Entity/TorrentDownloadFile.php @@ -0,0 +1,72 @@ +id; + } + + public function setId(string $id): static + { + $this->id = $id; + + return $this; + } + + public function getTorrentId(): ?int + { + return $this->torrentId; + } + + public function setTorrentId(int $torrentId): static + { + $this->torrentId = $torrentId; + + return $this; + } + + public function getUserId(): ?int + { + return $this->userId; + } + + public function setUserId(int $userId): static + { + $this->userId = $userId; + + return $this; + } + + public function getAdded(): ?int + { + return $this->added; + } + + public function setAdded(int $added): static + { + $this->added = $added; + + return $this; + } +} diff --git a/src/Entity/TorrentDownloadMagnet.php b/src/Entity/TorrentDownloadMagnet.php new file mode 100644 index 0000000..1b56cad --- /dev/null +++ b/src/Entity/TorrentDownloadMagnet.php @@ -0,0 +1,72 @@ +id; + } + + public function setId(string $id): static + { + $this->id = $id; + + return $this; + } + + public function getTorrentId(): ?int + { + return $this->torrentId; + } + + public function setTorrentId(int $torrentId): static + { + $this->torrentId = $torrentId; + + return $this; + } + + public function getUserId(): ?int + { + return $this->userId; + } + + public function setUserId(int $userId): static + { + $this->userId = $userId; + + return $this; + } + + public function getAdded(): ?int + { + return $this->added; + } + + public function setAdded(int $added): static + { + $this->added = $added; + + return $this; + } +} diff --git a/src/Repository/TorrentDownloadFileRepository.php b/src/Repository/TorrentDownloadFileRepository.php new file mode 100644 index 0000000..69b6140 --- /dev/null +++ b/src/Repository/TorrentDownloadFileRepository.php @@ -0,0 +1,53 @@ + + * + * @method TorrentDownloadFile|null find($id, $lockMode = null, $lockVersion = null) + * @method TorrentDownloadFile|null findOneBy(array $criteria, array $orderBy = null) + * @method TorrentDownloadFile[] findAll() + * @method TorrentDownloadFile[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class TorrentDownloadFileRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, TorrentDownloadFile::class); + } + + public function findTorrentDownloadFile( + int $torrentId, + int $userId + ): ?TorrentDownloadFile + { + return $this->createQueryBuilder('tdf') + ->where('tdf.torrentId = :torrentId') + ->andWhere('tdf.userId = :userId') + ->setParameter('torrentId', $torrentId) + ->setParameter('userId', $userId) + ->orderBy('tdf.id', 'DESC') // same to ts.added + ->setMaxResults(1) + ->getQuery() + ->getOneOrNullResult() + ; + } + + public function findTorrentDownloadFilesTotalByTorrentId( + int $torrentId + ): int + { + return $this->createQueryBuilder('tdf') + ->select('count(tdf.id)') + ->where('tdf.torrentId = :torrentId') + ->setParameter('torrentId', $torrentId) + ->getQuery() + ->getSingleScalarResult() + ; + } +} diff --git a/src/Repository/TorrentDownloadMagnetRepository.php b/src/Repository/TorrentDownloadMagnetRepository.php new file mode 100644 index 0000000..42612d0 --- /dev/null +++ b/src/Repository/TorrentDownloadMagnetRepository.php @@ -0,0 +1,53 @@ + + * + * @method TorrentDownloadMagnet|null find($id, $lockMode = null, $lockVersion = null) + * @method TorrentDownloadMagnet|null findOneBy(array $criteria, array $orderBy = null) + * @method TorrentDownloadMagnet[] findAll() + * @method TorrentDownloadMagnet[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class TorrentDownloadMagnetRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, TorrentDownloadMagnet::class); + } + + public function findTorrentDownloadMagnet( + int $torrentId, + int $userId + ): ?TorrentDownloadMagnet + { + return $this->createQueryBuilder('tdm') + ->where('tdm.torrentId = :torrentId') + ->andWhere('tdm.userId = :userId') + ->setParameter('torrentId', $torrentId) + ->setParameter('userId', $userId) + ->orderBy('tdm.id', 'DESC') // same to ts.added + ->setMaxResults(1) + ->getQuery() + ->getOneOrNullResult() + ; + } + + public function findTorrentDownloadMagnetsTotalByTorrentId( + int $torrentId + ): int + { + return $this->createQueryBuilder('tdm') + ->select('count(tdm.id)') + ->where('tdm.torrentId = :torrentId') + ->setParameter('torrentId', $torrentId) + ->getQuery() + ->getSingleScalarResult() + ; + } +} diff --git a/src/Service/TorrentService.php b/src/Service/TorrentService.php index cc89430..3bfa4d5 100644 --- a/src/Service/TorrentService.php +++ b/src/Service/TorrentService.php @@ -6,11 +6,15 @@ use App\Entity\Torrent; use App\Entity\TorrentLocales; use App\Entity\TorrentSensitive; use App\Entity\TorrentBookmark; +use App\Entity\TorrentDownloadFile; +use App\Entity\TorrentDownloadMagnet; use App\Repository\TorrentRepository; use App\Repository\TorrentLocalesRepository; use App\Repository\TorrentSensitiveRepository; use App\Repository\TorrentBookmarkRepository; +use App\Repository\TorrentDownloadFileRepository; +use App\Repository\TorrentDownloadMagnetRepository; use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\Filesystem\Filesystem; @@ -108,160 +112,6 @@ class TorrentService ); } - // Getters - public function getTorrent(int $id): ?Torrent - { - return $this->entityManagerInterface - ->getRepository(Torrent::class) - ->findOneByIdField($id); - } - - /// Locales - public function getTorrentLocales(int $id): ?TorrentLocales - { - return $this->entityManagerInterface - ->getRepository(TorrentLocales::class) - ->getTorrentLocales($id); - } - - public function findLastTorrentLocales(int $torrentId): ?TorrentLocales - { - return $this->entityManagerInterface - ->getRepository(TorrentLocales::class) - ->findLastTorrentLocales($torrentId); - } - - public function findTorrentLocales(int $torrentId): array - { - return $this->entityManagerInterface - ->getRepository(TorrentLocales::class) - ->findTorrentLocales($torrentId); - } - - /// Sensitive - public function getTorrentSensitive(int $id): ?TorrentSensitive - { - return $this->entityManagerInterface - ->getRepository(TorrentSensitive::class) - ->getTorrentSensitive($id); - } - - public function findLastTorrentSensitive(int $torrentId): ?TorrentSensitive - { - return $this->entityManagerInterface - ->getRepository(TorrentSensitive::class) - ->findLastTorrentSensitive($torrentId); - } - - public function findTorrentSensitive(int $torrentId): array - { - return $this->entityManagerInterface - ->getRepository(TorrentSensitive::class) - ->findTorrentSensitive($torrentId); - } - - /// Bookmark - public function findTorrentBookmark( - int $torrentId, - int $userId - ): ?TorrentBookmark - { - return $this->entityManagerInterface - ->getRepository(TorrentBookmark::class) - ->findTorrentBookmark($torrentId, $userId); - } - - public function findTorrentBookmarksTotalByTorrentId(int $torrentId): int - { - return $this->entityManagerInterface - ->getRepository(TorrentBookmark::class) - ->findTorrentBookmarksTotalByTorrentId($torrentId); - } - - // Update - public function toggleTorrentLocalesApproved( - int $id - ): ?TorrentLocales - { - $torrentLocales = $this->getTorrentLocales($id); - - $torrentLocales->setApproved( - !$torrentLocales->isApproved() // toggle current value - ); - - $this->entityManagerInterface->persist($torrentLocales); - $this->entityManagerInterface->flush(); - - return $torrentLocales; - } - - public function toggleTorrentSensitiveApproved( - int $id - ): ?TorrentSensitive - { - $torrentSensitive = $this->getTorrentSensitive($id); - - $torrentSensitive->setApproved( - !$torrentSensitive->isApproved() // toggle current value - ); - - $this->entityManagerInterface->persist($torrentSensitive); - $this->entityManagerInterface->flush(); - - return $torrentSensitive; - } - - // Delete - public function deleteTorrentLocales( - int $id - ): ?TorrentLocales - { - $torrentLocales = $this->getTorrentLocales($id); - - $this->entityManagerInterface->remove($torrentLocales); - $this->entityManagerInterface->flush(); - - return $torrentLocales; - } - - public function toggleTorrentBookmark( - int $torrentId, - int $userId, - int $added - ): void - { - if ($torrentBookmark = $this->findTorrentBookmark($torrentId, $userId)) - { - $this->entityManagerInterface->remove($torrentBookmark); - $this->entityManagerInterface->flush(); - } - - else - { - $torrentBookmark = new TorrentBookmark(); - - $torrentBookmark->setTorrentId($torrentId); - $torrentBookmark->setUserId($userId); - $torrentBookmark->setAdded($added); - - $this->entityManagerInterface->persist($torrentBookmark); - $this->entityManagerInterface->flush(); - } - } - - public function deleteTorrentSensitive( - int $id - ): ?TorrentSensitive - { - $torrentSensitive = $this->getTorrentSensitive($id); - - $this->entityManagerInterface->remove($torrentSensitive); - $this->entityManagerInterface->flush(); - - return $torrentSensitive; - } - - // Setters public function add( string $filepath, int $userId, @@ -310,6 +160,14 @@ class TorrentService return $torrent; } + // Torrent + public function getTorrent(int $id): ?Torrent + { + return $this->entityManagerInterface + ->getRepository(Torrent::class) + ->findOneByIdField($id); + } + public function addTorrent( int $userId, int $added, @@ -330,6 +188,56 @@ class TorrentService return $torrent; } + // Torrent locale + public function getTorrentLocales(int $id): ?TorrentLocales + { + return $this->entityManagerInterface + ->getRepository(TorrentLocales::class) + ->getTorrentLocales($id); + } + + public function findLastTorrentLocales(int $torrentId): ?TorrentLocales + { + return $this->entityManagerInterface + ->getRepository(TorrentLocales::class) + ->findLastTorrentLocales($torrentId); + } + + public function findTorrentLocales(int $torrentId): array + { + return $this->entityManagerInterface + ->getRepository(TorrentLocales::class) + ->findTorrentLocales($torrentId); + } + + public function toggleTorrentLocalesApproved( + int $id + ): ?TorrentLocales + { + $torrentLocales = $this->getTorrentLocales($id); + + $torrentLocales->setApproved( + !$torrentLocales->isApproved() // toggle current value + ); + + $this->entityManagerInterface->persist($torrentLocales); + $this->entityManagerInterface->flush(); + + return $torrentLocales; + } + + public function deleteTorrentLocales( + int $id + ): ?TorrentLocales + { + $torrentLocales = $this->getTorrentLocales($id); + + $this->entityManagerInterface->remove($torrentLocales); + $this->entityManagerInterface->flush(); + + return $torrentLocales; + } + public function addTorrentLocales( int $torrentId, int $userId, @@ -352,6 +260,56 @@ class TorrentService return $torrentLocales; } + // Torrent sensitive + public function getTorrentSensitive(int $id): ?TorrentSensitive + { + return $this->entityManagerInterface + ->getRepository(TorrentSensitive::class) + ->getTorrentSensitive($id); + } + + public function findLastTorrentSensitive(int $torrentId): ?TorrentSensitive + { + return $this->entityManagerInterface + ->getRepository(TorrentSensitive::class) + ->findLastTorrentSensitive($torrentId); + } + + public function findTorrentSensitive(int $torrentId): array + { + return $this->entityManagerInterface + ->getRepository(TorrentSensitive::class) + ->findTorrentSensitive($torrentId); + } + + public function toggleTorrentSensitiveApproved( + int $id + ): ?TorrentSensitive + { + $torrentSensitive = $this->getTorrentSensitive($id); + + $torrentSensitive->setApproved( + !$torrentSensitive->isApproved() // toggle current value + ); + + $this->entityManagerInterface->persist($torrentSensitive); + $this->entityManagerInterface->flush(); + + return $torrentSensitive; + } + + public function deleteTorrentSensitive( + int $id + ): ?TorrentSensitive + { + $torrentSensitive = $this->getTorrentSensitive($id); + + $this->entityManagerInterface->remove($torrentSensitive); + $this->entityManagerInterface->flush(); + + return $torrentSensitive; + } + public function addTorrentSensitive( int $torrentId, int $userId, @@ -373,4 +331,121 @@ class TorrentService return $torrentSensitive; } + + // Torrent bookmark + public function findTorrentBookmark( + int $torrentId, + int $userId + ): ?TorrentBookmark + { + return $this->entityManagerInterface + ->getRepository(TorrentBookmark::class) + ->findTorrentBookmark($torrentId, $userId); + } + + public function findTorrentBookmarksTotalByTorrentId(int $torrentId): int + { + return $this->entityManagerInterface + ->getRepository(TorrentBookmark::class) + ->findTorrentBookmarksTotalByTorrentId($torrentId); + } + + public function toggleTorrentBookmark( + int $torrentId, + int $userId, + int $added + ): void + { + if ($torrentBookmark = $this->findTorrentBookmark($torrentId, $userId)) + { + $this->entityManagerInterface->remove($torrentBookmark); + $this->entityManagerInterface->flush(); + } + + else + { + $torrentBookmark = new TorrentBookmark(); + + $torrentBookmark->setTorrentId($torrentId); + $torrentBookmark->setUserId($userId); + $torrentBookmark->setAdded($added); + + $this->entityManagerInterface->persist($torrentBookmark); + $this->entityManagerInterface->flush(); + } + } + + // Torrent download file + public function findTorrentDownloadFile( + int $torrentId, + int $userId + ): ?TorrentDownloadFile + { + return $this->entityManagerInterface + ->getRepository(TorrentDownloadFile::class) + ->findTorrentDownloadFile($torrentId, $userId); + } + + public function findTorrentDownloadFilesTotalByTorrentId(int $torrentId): int + { + return $this->entityManagerInterface + ->getRepository(TorrentDownloadFile::class) + ->findTorrentDownloadFilesTotalByTorrentId($torrentId); + } + + public function registerTorrentDownloadFile( + int $torrentId, + int $userId, + int $added + ): void + { + if (!$this->findTorrentDownloadFile($torrentId, $userId)) + { + $torrentDownloadFile = new TorrentDownloadFile(); + + $torrentDownloadFile->setTorrentId($torrentId); + $torrentDownloadFile->setUserId($userId); + $torrentDownloadFile->setAdded($added); + + $this->entityManagerInterface->persist($torrentDownloadFile); + $this->entityManagerInterface->flush(); + } + } + + // Torrent download magnet + public function findTorrentDownloadMagnet( + int $torrentId, + int $userId + ): ?TorrentDownloadMagnet + { + return $this->entityManagerInterface + ->getRepository(TorrentDownloadMagnet::class) + ->findTorrentDownloadMagnet($torrentId, $userId); + } + + public function findTorrentDownloadMagnetsTotalByTorrentId(int $torrentId): int + { + return $this->entityManagerInterface + ->getRepository(TorrentDownloadMagnet::class) + ->findTorrentDownloadMagnetsTotalByTorrentId($torrentId); + } + + public function registerTorrentDownloadMagnet( + int $torrentId, + int $userId, + int $added + ): void + { + if (!$this->findTorrentDownloadMagnet($torrentId, $userId)) + { + $torrentDownloadMagnet = new TorrentDownloadMagnet(); + + $torrentDownloadMagnet->setTorrentId($torrentId); + $torrentDownloadMagnet->setUserId($userId); + $torrentDownloadMagnet->setAdded($added); + + $this->entityManagerInterface->persist($torrentDownloadMagnet); + $this->entityManagerInterface->flush(); + } + } } \ No newline at end of file diff --git a/templates/default/torrent/info.html.twig b/templates/default/torrent/info.html.twig index e731e2a..7ee1735 100644 --- a/templates/default/torrent/info.html.twig +++ b/templates/default/torrent/info.html.twig @@ -28,8 +28,36 @@ {{ 'Torrent'|trans }} #{{ torrent.id }}
- - {% if torrent.bookmark.active %} + + {% if torrent.download.magnet.exist %} + + + + {% else %} + + + + {% endif %} + + + {{ torrent.download.magnet.total }} + + + {% if torrent.download.file.exist %} + + + + {% else %} + + + + {% endif %} + + + {{ torrent.download.file.total }} + + + {% if torrent.bookmark.exist %} @@ -39,16 +67,12 @@ {% endif %} - + {{ torrent.bookmark.total }}
{# - - - - - +