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 | 361 +++++++++++------- templates/default/torrent/info.html.twig | 40 +- 7 files changed, 743 insertions(+), 210 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,7 +112,55 @@ class TorrentService ); } - // Getters + public function add( + string $filepath, + int $userId, + int $added, + array $locales, + bool $sensitive, + bool $approved + ): ?Torrent + { + $torrent = $this->addTorrent( + $userId, + $added, + $this->generateTorrentKeywordsByTorrentFilepath( + $filepath + ), + $approved + ); + + $filesystem = new Filesystem(); + $filesystem->copy( + $filepath, + $this->getStorageFilepathById( + $torrent->getId() + ) + ); + + if (!empty($locales)) + { + $this->addTorrentLocales( + $torrent->getId(), + $userId, + $added, + $locales, + $approved + ); + } + + $this->addTorrentSensitive( + $torrent->getId(), + $userId, + $added, + $sensitive, + $approved + ); + + return $torrent; + } + + // Torrent public function getTorrent(int $id): ?Torrent { return $this->entityManagerInterface @@ -116,7 +168,27 @@ class TorrentService ->findOneByIdField($id); } - /// Locales + public function addTorrent( + int $userId, + int $added, + string $keywords, + bool $approved + ): ?Torrent + { + $torrent = new Torrent(); + + $torrent->setUserId($userId); + $torrent->setAdded($added); + $torrent->setKeywords($keywords); + $torrent->setApproved($approved); + + $this->entityManagerInterface->persist($torrent); + $this->entityManagerInterface->flush(); + + return $torrent; + } + + // Torrent locale public function getTorrentLocales(int $id): ?TorrentLocales { return $this->entityManagerInterface @@ -138,7 +210,57 @@ class TorrentService ->findTorrentLocales($torrentId); } - /// Sensitive + 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, + int $added, + array $value, + bool $approved + ): ?TorrentLocales + { + $torrentLocales = new TorrentLocales(); + + $torrentLocales->setTorrentId($torrentId); + $torrentLocales->setUserId($userId); + $torrentLocales->setAdded($added); + $torrentLocales->setValue($value); + $torrentLocales->setApproved($approved); + + $this->entityManagerInterface->persist($torrentLocales); + $this->entityManagerInterface->flush(); + + return $torrentLocales; + } + + // Torrent sensitive public function getTorrentSensitive(int $id): ?TorrentSensitive { return $this->entityManagerInterface @@ -160,68 +282,72 @@ class TorrentService ->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( + public function toggleTorrentSensitiveApproved( int $id - ): ?TorrentLocales + ): ?TorrentSensitive { - $torrentLocales = $this->getTorrentLocales($id); + $torrentSensitive = $this->getTorrentSensitive($id); - $torrentLocales->setApproved( - !$torrentLocales->isApproved() // toggle current value + $torrentSensitive->setApproved( + !$torrentSensitive->isApproved() // toggle current value ); - $this->entityManagerInterface->persist($torrentLocales); + $this->entityManagerInterface->persist($torrentSensitive); $this->entityManagerInterface->flush(); - return $torrentLocales; + return $torrentSensitive; } - public function toggleTorrentSensitiveApproved( + public function deleteTorrentSensitive( int $id ): ?TorrentSensitive { $torrentSensitive = $this->getTorrentSensitive($id); - $torrentSensitive->setApproved( - !$torrentSensitive->isApproved() // toggle current value - ); - - $this->entityManagerInterface->persist($torrentSensitive); + $this->entityManagerInterface->remove($torrentSensitive); $this->entityManagerInterface->flush(); return $torrentSensitive; } - // Delete - public function deleteTorrentLocales( - int $id - ): ?TorrentLocales + public function addTorrentSensitive( + int $torrentId, + int $userId, + int $added, + bool $value, + bool $approved + ): ?TorrentSensitive { - $torrentLocales = $this->getTorrentLocales($id); + $torrentSensitive = new TorrentSensitive(); - $this->entityManagerInterface->remove($torrentLocales); + $torrentSensitive->setTorrentId($torrentId); + $torrentSensitive->setUserId($userId); + $torrentSensitive->setAdded($added); + $torrentSensitive->setValue($value); + $torrentSensitive->setApproved($approved); + + $this->entityManagerInterface->persist($torrentSensitive); $this->entityManagerInterface->flush(); - return $torrentLocales; + 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( @@ -249,128 +375,77 @@ class TorrentService } } - public function deleteTorrentSensitive( - int $id - ): ?TorrentSensitive + // Torrent download file + public function findTorrentDownloadFile( + int $torrentId, + int $userId + ): ?TorrentDownloadFile { - $torrentSensitive = $this->getTorrentSensitive($id); - - $this->entityManagerInterface->remove($torrentSensitive); - $this->entityManagerInterface->flush(); - - return $torrentSensitive; + return $this->entityManagerInterface + ->getRepository(TorrentDownloadFile::class) + ->findTorrentDownloadFile($torrentId, $userId); } - // Setters - public function add( - string $filepath, - int $userId, - int $added, - array $locales, - bool $sensitive, - bool $approved - ): ?Torrent + public function findTorrentDownloadFilesTotalByTorrentId(int $torrentId): int { - $torrent = $this->addTorrent( - $userId, - $added, - $this->generateTorrentKeywordsByTorrentFilepath( - $filepath - ), - $approved - ); - - $filesystem = new Filesystem(); - $filesystem->copy( - $filepath, - $this->getStorageFilepathById( - $torrent->getId() - ) - ); - - if (!empty($locales)) - { - $this->addTorrentLocales( - $torrent->getId(), - $userId, - $added, - $locales, - $approved - ); - } - - $this->addTorrentSensitive( - $torrent->getId(), - $userId, - $added, - $sensitive, - $approved - ); - - return $torrent; + return $this->entityManagerInterface + ->getRepository(TorrentDownloadFile::class) + ->findTorrentDownloadFilesTotalByTorrentId($torrentId); } - public function addTorrent( + public function registerTorrentDownloadFile( + int $torrentId, int $userId, - int $added, - string $keywords, - bool $approved - ): ?Torrent + int $added + ): void { - $torrent = new Torrent(); - - $torrent->setUserId($userId); - $torrent->setAdded($added); - $torrent->setKeywords($keywords); - $torrent->setApproved($approved); + if (!$this->findTorrentDownloadFile($torrentId, $userId)) + { + $torrentDownloadFile = new TorrentDownloadFile(); - $this->entityManagerInterface->persist($torrent); - $this->entityManagerInterface->flush(); + $torrentDownloadFile->setTorrentId($torrentId); + $torrentDownloadFile->setUserId($userId); + $torrentDownloadFile->setAdded($added); - return $torrent; + $this->entityManagerInterface->persist($torrentDownloadFile); + $this->entityManagerInterface->flush(); + } } - public function addTorrentLocales( + // Torrent download magnet + public function findTorrentDownloadMagnet( int $torrentId, - int $userId, - int $added, - array $value, - bool $approved - ): ?TorrentLocales + int $userId + ): ?TorrentDownloadMagnet { - $torrentLocales = new TorrentLocales(); - - $torrentLocales->setTorrentId($torrentId); - $torrentLocales->setUserId($userId); - $torrentLocales->setAdded($added); - $torrentLocales->setValue($value); - $torrentLocales->setApproved($approved); - - $this->entityManagerInterface->persist($torrentLocales); - $this->entityManagerInterface->flush(); + return $this->entityManagerInterface + ->getRepository(TorrentDownloadMagnet::class) + ->findTorrentDownloadMagnet($torrentId, $userId); + } - return $torrentLocales; + public function findTorrentDownloadMagnetsTotalByTorrentId(int $torrentId): int + { + return $this->entityManagerInterface + ->getRepository(TorrentDownloadMagnet::class) + ->findTorrentDownloadMagnetsTotalByTorrentId($torrentId); } - public function addTorrentSensitive( + public function registerTorrentDownloadMagnet( int $torrentId, int $userId, - int $added, - bool $value, - bool $approved - ): ?TorrentSensitive + int $added + ): void { - $torrentSensitive = new TorrentSensitive(); - - $torrentSensitive->setTorrentId($torrentId); - $torrentSensitive->setUserId($userId); - $torrentSensitive->setAdded($added); - $torrentSensitive->setValue($value); - $torrentSensitive->setApproved($approved); + if (!$this->findTorrentDownloadMagnet($torrentId, $userId)) + { + $torrentDownloadMagnet = new TorrentDownloadMagnet(); - $this->entityManagerInterface->persist($torrentSensitive); - $this->entityManagerInterface->flush(); + $torrentDownloadMagnet->setTorrentId($torrentId); + $torrentDownloadMagnet->setUserId($userId); + $torrentDownloadMagnet->setAdded($added); - return $torrentSensitive; + $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 }}
{# - - - - - +