Added:
- Updated Reader - fix image download for JavascriptScraper.php
This commit is contained in:
@@ -213,38 +213,38 @@ class MangaController extends AbstractController
|
||||
return new JsonResponse(['success' => 'Chapter hidden.'], 200);
|
||||
}
|
||||
|
||||
#[Route('/manga/read/{mangaSlug}/{chapterNumber}/{pageNumber}', name: 'app_manga_read')]
|
||||
public function readChapterPage(string $mangaSlug, float $chapterNumber, int $pageNumber = 1): Response
|
||||
{
|
||||
$manga = $this->mangaRepository->findOneBy(['slug' => $mangaSlug]);
|
||||
if (!$manga) {
|
||||
throw $this->createNotFoundException("Le manga demandé n'existe pas.");
|
||||
}
|
||||
|
||||
$chapter = $manga->getChapterByNumber($chapterNumber);
|
||||
if (!$chapter) {
|
||||
throw $this->createNotFoundException("Le chapitre demandé n'existe pas.");
|
||||
}
|
||||
|
||||
if (is_null($chapter->getCbzPath())) {
|
||||
throw $this->createNotFoundException("Le chapitre demandé n'a pas été scrapé.");
|
||||
}
|
||||
|
||||
$pageContent = $this->cbzService->getPageContent($chapter->getCbzPath(), $pageNumber);
|
||||
if (!$pageContent) {
|
||||
throw $this->createNotFoundException("La page demandée n'existe pas.");
|
||||
}
|
||||
|
||||
$totalPages = $this->cbzService->getPageCount($chapter->getCbzPath());
|
||||
|
||||
return $this->render('manga/manga_reader.html.twig', [
|
||||
'manga' => $manga,
|
||||
'chapter' => $chapter,
|
||||
'currentPage' => $pageNumber,
|
||||
'totalPages' => $totalPages,
|
||||
'pageContent' => base64_encode($pageContent),
|
||||
]);
|
||||
}
|
||||
// #[Route('/manga/read/{mangaSlug}/{chapterNumber}/{pageNumber}', name: 'app_manga_read')]
|
||||
// public function readChapterPage(string $mangaSlug, float $chapterNumber, int $pageNumber = 1): Response
|
||||
// {
|
||||
// $manga = $this->mangaRepository->findOneBy(['slug' => $mangaSlug]);
|
||||
// if (!$manga) {
|
||||
// throw $this->createNotFoundException("Le manga demandé n'existe pas.");
|
||||
// }
|
||||
//
|
||||
// $chapter = $manga->getChapterByNumber($chapterNumber);
|
||||
// if (!$chapter) {
|
||||
// throw $this->createNotFoundException("Le chapitre demandé n'existe pas.");
|
||||
// }
|
||||
//
|
||||
// if (is_null($chapter->getCbzPath())) {
|
||||
// throw $this->createNotFoundException("Le chapitre demandé n'a pas été scrapé.");
|
||||
// }
|
||||
//
|
||||
// $pageContent = $this->cbzService->getPageContent($chapter->getCbzPath(), $pageNumber);
|
||||
// if (!$pageContent) {
|
||||
// throw $this->createNotFoundException("La page demandée n'existe pas.");
|
||||
// }
|
||||
//
|
||||
// $totalPages = $this->cbzService->getPageCount($chapter->getCbzPath());
|
||||
//
|
||||
// return $this->render('manga/manga_reader.html.twig', [
|
||||
// 'manga' => $manga,
|
||||
// 'chapter' => $chapter,
|
||||
// 'currentPage' => $pageNumber,
|
||||
// 'totalPages' => $totalPages,
|
||||
// 'pageContent' => base64_encode($pageContent),
|
||||
// ]);
|
||||
// }
|
||||
|
||||
#[Route('/manga/search/{query}', name: 'app_manga_search')]
|
||||
public function search(string $query = ''): Response
|
||||
|
||||
130
src/Controller/ReaderController.php
Normal file
130
src/Controller/ReaderController.php
Normal file
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Repository\MangaRepository;
|
||||
use App\Service\CbzService;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
class ReaderController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly MangaRepository $mangaRepository,
|
||||
private readonly CbzService $cbzService
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
#[Route('/read/{mangaSlug}/{chapterNumber}', name: 'app_reader')]
|
||||
public function read(string $mangaSlug, float $chapterNumber): Response
|
||||
{
|
||||
$manga = $this->mangaRepository->findOneBy(['slug' => $mangaSlug]);
|
||||
if (!$manga) {
|
||||
throw $this->createNotFoundException("Le manga demandé n'existe pas.");
|
||||
}
|
||||
|
||||
$chapter = $manga->getChapterByNumber($chapterNumber);
|
||||
if (!$chapter) {
|
||||
throw $this->createNotFoundException("Le chapitre demandé n'existe pas.");
|
||||
}
|
||||
|
||||
if (is_null($chapter->getCbzPath())) {
|
||||
throw $this->createNotFoundException("Le chapitre demandé n'a pas été scrapé.");
|
||||
}
|
||||
|
||||
$totalPages = $this->cbzService->getPageCount($chapter->getCbzPath());
|
||||
|
||||
return $this->render('reader/index.html.twig', [
|
||||
'manga' => $manga,
|
||||
'chapter' => $chapter,
|
||||
'totalPages' => $totalPages,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/api/read/{mangaSlug}/{chapterNumber}/{pageNumber}', name: 'app_reader_page')]
|
||||
public function getPage(string $mangaSlug, float $chapterNumber, int $pageNumber): Response
|
||||
{
|
||||
$manga = $this->mangaRepository->findOneBy(['slug' => $mangaSlug]);
|
||||
if (!$manga) {
|
||||
throw $this->createNotFoundException("Le manga demandé n'existe pas.");
|
||||
}
|
||||
|
||||
$chapter = $manga->getChapterByNumber($chapterNumber);
|
||||
if (!$chapter) {
|
||||
throw $this->createNotFoundException("Le chapitre demandé n'existe pas.");
|
||||
}
|
||||
|
||||
$pageContent = $this->cbzService->getPageContent($chapter->getCbzPath(), $pageNumber);
|
||||
if (!$pageContent) {
|
||||
throw $this->createNotFoundException("La page demandée n'existe pas.");
|
||||
}
|
||||
|
||||
return new Response(base64_encode($pageContent), 200, ['Content-Type' => 'text/plain']);
|
||||
}
|
||||
|
||||
#[Route('/api/chapters/{mangaSlug}', name: 'app_reader_chapters')]
|
||||
public function getChapters(string $mangaSlug): JsonResponse
|
||||
{
|
||||
$manga = $this->mangaRepository->findOneBy(['slug' => $mangaSlug]);
|
||||
if (!$manga) {
|
||||
throw $this->createNotFoundException("Le manga demandé n'existe pas.");
|
||||
}
|
||||
|
||||
$chapters = $manga->getChapters()
|
||||
->filter(fn($chapter) => $chapter->isVisible() && !is_null($chapter->getCbzPath()))
|
||||
->toArray();
|
||||
|
||||
usort($chapters, fn($a, $b) => $b->getNumber() <=> $a->getNumber());
|
||||
|
||||
$chapters = array_values(array_map(fn($chapter) => [
|
||||
'number' => $chapter->getNumber(),
|
||||
'title' => $chapter->getTitle()
|
||||
], $chapters));
|
||||
|
||||
|
||||
return $this->json($chapters);
|
||||
}
|
||||
|
||||
#[Route('/api/previous-chapter/{mangaSlug}/{currentChapterNumber}', name: 'app_reader_previous_chapter')]
|
||||
public function getPreviousChapter(string $mangaSlug, float $currentChapterNumber): JsonResponse
|
||||
{
|
||||
$manga = $this->mangaRepository->findOneBy(['slug' => $mangaSlug]);
|
||||
if (!$manga) {
|
||||
throw $this->createNotFoundException("Le manga demandé n'existe pas.");
|
||||
}
|
||||
|
||||
$previousChapter = $manga->getChapters()
|
||||
->filter(fn($chapter) => $chapter->isVisible() && $chapter->getNumber() < $currentChapterNumber)
|
||||
->last();
|
||||
|
||||
return $this->json($previousChapter ? [
|
||||
'number' => $previousChapter->getNumber(),
|
||||
'title' => $previousChapter->getTitle()
|
||||
] : null);
|
||||
}
|
||||
|
||||
#[Route('/api/next-chapter/{mangaSlug}/{currentChapterNumber}', name: 'app_reader_next_chapter')]
|
||||
public function getNextChapter(string $mangaSlug, float $currentChapterNumber): JsonResponse
|
||||
{
|
||||
$manga = $this->mangaRepository->findOneBy(['slug' => $mangaSlug]);
|
||||
if (!$manga) {
|
||||
throw $this->createNotFoundException("Le manga demandé n'existe pas.");
|
||||
}
|
||||
|
||||
$nextChapter = $manga->getChapters()
|
||||
->filter(fn($chapter) => $chapter->isVisible() && $chapter->getNumber() > $currentChapterNumber)
|
||||
->toArray();
|
||||
|
||||
usort($nextChapter, fn($a, $b) => $a->getNumber() <=> $b->getNumber());
|
||||
|
||||
$nextChapter = reset($nextChapter) ?: null;
|
||||
|
||||
return $this->json($nextChapter ? [
|
||||
'number' => $nextChapter->getNumber(),
|
||||
'title' => $nextChapter->getTitle()
|
||||
] : null);
|
||||
}
|
||||
}
|
||||
@@ -57,6 +57,7 @@ readonly class DownloadChapterHandler
|
||||
->setChapterUrlFormat('at-home/server/%s')
|
||||
->setScrapingType('mangadex');
|
||||
|
||||
|
||||
// (new ContentSource())
|
||||
// ->setBaseUrl('https://lelscans.net')
|
||||
// ->setImageSelector('#image img')
|
||||
|
||||
@@ -7,6 +7,7 @@ use App\Entity\ContentSource;
|
||||
use App\Entity\Manga;
|
||||
use App\Event\PageScrappingProgressEvent;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Exception;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
@@ -107,4 +108,24 @@ abstract class AbstractScraper implements ScraperInterface
|
||||
$event = new PageScrappingProgressEvent($chapter->getId(), $currentPage, $totalPages);
|
||||
$this->eventDispatcher->dispatch($event, PageScrappingProgressEvent::NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function downloadAndSaveImage(string $imageUrl, string $destinationPath): void
|
||||
{
|
||||
try {
|
||||
$response = $this->httpClient->get($imageUrl);
|
||||
$contentType = $response->getHeaderLine('Content-Type');
|
||||
|
||||
if (str_starts_with($contentType, 'image/')) {
|
||||
file_put_contents($destinationPath, $response->getBody()->getContents());
|
||||
} else {
|
||||
throw new Exception('Le contenu récupéré n\'est pas une image. Type de contenu : ' . $contentType);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('Erreur lors de la récupération de l\'image : ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use App\Entity\ContentSource;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Exception;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\DomCrawler\Crawler;
|
||||
|
||||
@@ -25,6 +26,7 @@ class HtmlScraper extends AbstractScraper
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function scrapeChapter(Chapter $chapter, ContentSource $contentSource): array|bool
|
||||
{
|
||||
@@ -116,6 +118,9 @@ class HtmlScraper extends AbstractScraper
|
||||
return $pageData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
private function scrapeHorizontalReader(string $chapterUrl, ContentSource $contentSource): array
|
||||
{
|
||||
$pageData = [];
|
||||
@@ -156,22 +161,6 @@ class HtmlScraper extends AbstractScraper
|
||||
}
|
||||
}
|
||||
|
||||
private function downloadAndSaveImage(string $imageUrl, string $destinationPath): void
|
||||
{
|
||||
try {
|
||||
$response = $this->client->get($imageUrl);
|
||||
$contentType = $response->getHeaderLine('Content-Type');
|
||||
|
||||
if (str_starts_with($contentType, 'image/')) {
|
||||
file_put_contents($destinationPath, $response->getBody()->getContents());
|
||||
} else {
|
||||
throw new Exception('Le contenu récupéré n\'est pas une image. Type de contenu : ' . $contentType);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('Erreur lors de la récupération de l\'image : ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private function extractMangaPageData(string $html, ContentSource $mangaSource): array
|
||||
{
|
||||
$crawler = new Crawler($html);
|
||||
|
||||
@@ -5,10 +5,14 @@ namespace App\Service\Scraper;
|
||||
use App\Entity\Chapter;
|
||||
use App\Entity\ContentSource;
|
||||
use Exception;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Symfony\Component\Panther\Client as PantherClient;
|
||||
|
||||
class JavascriptScraper extends AbstractScraper
|
||||
{
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function scrapeChapter(Chapter $chapter, ContentSource $contentSource): array|bool
|
||||
{
|
||||
$manga = $chapter->getManga();
|
||||
@@ -36,7 +40,7 @@ class JavascriptScraper extends AbstractScraper
|
||||
$imageName = sprintf('%03d.%s', $index + 1, pathinfo(parse_url($page['image_url'], PHP_URL_PATH), PATHINFO_EXTENSION));
|
||||
$imagePath = $tempDir . '/' . $imageName;
|
||||
|
||||
file_put_contents($imagePath, file_get_contents($page['image_url']));
|
||||
$this->downloadAndSaveImage($page['image_url'], $imagePath);
|
||||
$this->dispatchProgressEvent($chapter, $index + 1, count($pageData));
|
||||
|
||||
$page['local_image_url'] = $imagePath;
|
||||
@@ -52,9 +56,6 @@ class JavascriptScraper extends AbstractScraper
|
||||
$this->cleanupTempFiles($tempDir);
|
||||
|
||||
return $pageData;
|
||||
} catch (Exception $e) {
|
||||
// Log the error
|
||||
return false;
|
||||
} finally {
|
||||
$pantherClient->close();
|
||||
}
|
||||
|
||||
@@ -80,10 +80,4 @@ class MangadexScraper extends AbstractScraper
|
||||
{
|
||||
return $scrapingType === 'mangadex';
|
||||
}
|
||||
|
||||
private function downloadAndSaveImage(string $imageUrl, string $destinationPath): void
|
||||
{
|
||||
$response = $this->client->get($imageUrl);
|
||||
file_put_contents($destinationPath, $response->getBody()->getContents());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user