Compare commits
10 Commits
21b2adfa07
...
0482ec9f7f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0482ec9f7f | ||
|
|
9318d0a9a0 | ||
|
|
447f1fbe84 | ||
|
|
59cf4cd3c1 | ||
|
|
d62907a38c | ||
|
|
c6bd6ba549 | ||
|
|
d4142012ec | ||
|
|
8811d3dd5e | ||
|
|
2941bbecd1 | ||
|
|
5f15d14ae1 |
3
.env
3
.env
@@ -51,5 +51,4 @@ MERCURE_JWT_SECRET="Mangarr-JWT-Secret"
|
||||
###< symfony/mercure-bundle ###
|
||||
|
||||
#Custom
|
||||
MANGA_DATA_PATH=/mnt/c/Users/jerem/Mangas
|
||||
IMAGE_DATA_PATH=/mnt/c/Users/jerem/MangasImages
|
||||
MANGA_DATA_PATH=/home/ext.jeremy.guillot@maxicoffee.domains/Mangarr
|
||||
|
||||
@@ -30,8 +30,8 @@ export default class extends Controller {
|
||||
}
|
||||
|
||||
|
||||
const mercureHubUrl = 'https://localhost/.well-known/mercure';
|
||||
const eventSource = new EventSource(`${mercureHubUrl}?topic=activity`);
|
||||
const mercureHubUrl = 'https://mangarr.test.nestor-server.fr/.well-known/mercure';
|
||||
const eventSource = new EventSource(`${mercureHubUrl}?topic=activity`, {withCredentials: true});
|
||||
|
||||
eventSource.onmessage = (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
|
||||
@@ -11,8 +11,8 @@ export default class extends Controller {
|
||||
this.currentPage = 0;
|
||||
this.totalPages = 0;
|
||||
|
||||
const mercureHubUrl = 'https://localhost/.well-known/mercure';
|
||||
this.eventSource = new EventSource(`${mercureHubUrl}?topic=activity`);
|
||||
const mercureHubUrl = 'https://mangarr.test.nestor-server.fr/.well-known/mercure';
|
||||
this.eventSource = new EventSource(`${mercureHubUrl}?topic=activity`, {withCredentials: true});
|
||||
|
||||
this.eventSource.onmessage = this.handleMessage.bind(this);
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ export default class extends Controller {
|
||||
// ...
|
||||
connect() {
|
||||
const topic = this.data.get('topic');
|
||||
const mercureHubUrl = 'https://localhost/.well-known/mercure';
|
||||
const eventSource = new EventSource(`${mercureHubUrl}?topic=${topic}`);
|
||||
const mercureHubUrl = 'https://mangarr.test.nestor-server.fr/.well-known/mercure';
|
||||
const eventSource = new EventSource(`${mercureHubUrl}?topic=${topic}`, {withCredentials: true});
|
||||
|
||||
eventSource.onmessage = (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
|
||||
@@ -25,7 +25,7 @@ services:
|
||||
ports:
|
||||
# HTTP
|
||||
- target: 80
|
||||
published: ${HTTP_PORT:-80}
|
||||
published: ${HTTP_PORT:-8081}
|
||||
protocol: tcp
|
||||
# HTTPS
|
||||
- target: 443
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"php": ">=8.3.1",
|
||||
"ext-ctype": "*",
|
||||
"ext-curl": "*",
|
||||
"ext-gd": "*",
|
||||
"ext-iconv": "*",
|
||||
"ext-zip": "*",
|
||||
"api-platform/core": "^3.2",
|
||||
@@ -104,6 +105,7 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"dbrekelmans/bdi": "^1.3",
|
||||
"deployer/deployer": "^7.5",
|
||||
"doctrine/doctrine-fixtures-bundle": "^3.5",
|
||||
"friendsofphp/php-cs-fixer": "^3.48",
|
||||
"mtdowling/jmespath.php": "^2.7",
|
||||
|
||||
61
composer.lock
generated
61
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "2533b3293b9694632fddb7de78675af4",
|
||||
"content-hash": "6258706876617c8b0c08f13c5a158fe7",
|
||||
"packages": [
|
||||
{
|
||||
"name": "api-platform/core",
|
||||
@@ -9244,6 +9244,60 @@
|
||||
},
|
||||
"time": "2024-02-22T15:29:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "deployer/deployer",
|
||||
"version": "v7.5.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/deployphp/deployer.git",
|
||||
"reference": "4900fe799ce5566d54a14103cdfd6e865b7c5d72"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/deployphp/deployer/zipball/4900fe799ce5566d54a14103cdfd6e865b7c5d72",
|
||||
"reference": "4900fe799ce5566d54a14103cdfd6e865b7c5d72",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"php": "^8.0|^7.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^3.64",
|
||||
"pestphp/pest": "^3.3",
|
||||
"phpstan/phpstan": "^1.4",
|
||||
"phpunit/php-code-coverage": "^11.0",
|
||||
"phpunit/phpunit": "^11.4"
|
||||
},
|
||||
"bin": [
|
||||
"bin/dep"
|
||||
],
|
||||
"type": "library",
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Anton Medvedev",
|
||||
"email": "anton@medv.io"
|
||||
}
|
||||
],
|
||||
"description": "Deployment Tool",
|
||||
"homepage": "https://deployer.org",
|
||||
"support": {
|
||||
"docs": "https://deployer.org/docs",
|
||||
"issues": "https://github.com/deployphp/deployer/issues",
|
||||
"source": "https://github.com/deployphp/deployer"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/sponsors/antonmedv",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2024-11-27T21:35:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/data-fixtures",
|
||||
"version": "1.7.0",
|
||||
@@ -11998,16 +12052,17 @@
|
||||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"stability-flags": {},
|
||||
"prefer-stable": true,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": ">=8.3.1",
|
||||
"ext-ctype": "*",
|
||||
"ext-curl": "*",
|
||||
"ext-gd": "*",
|
||||
"ext-iconv": "*",
|
||||
"ext-zip": "*"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"platform-dev": {},
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
|
||||
39
deploy.php
Normal file
39
deploy.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
namespace Deployer;
|
||||
|
||||
require 'recipe/symfony.php';
|
||||
// require 'contrib/webpack_encore.php';
|
||||
require 'contrib/npm.php';
|
||||
|
||||
// Config
|
||||
set('nodejs_version', 'node_22.x');
|
||||
set('keep_releases', '3');
|
||||
set('repository', 'gitea@git.test.nestor-server.fr:Colgora/Mangarr.git');
|
||||
set('webpack_encore/env', 'production');
|
||||
set('composer_options', '--verbose --prefer-dist --no-progress --no-interaction --optimize-autoloader');
|
||||
|
||||
set('shared_files', ['.env.local','var/log/prod.log']);
|
||||
set('shared_dirs', ['config/secrets','public/cbz','public/tmp','public/images']);
|
||||
// add('writable_dirs', []);
|
||||
|
||||
desc('Runs webpack encore build');
|
||||
task('webpack_encore:build', function () {
|
||||
run("cd {{release_path}} && npm run build");
|
||||
});
|
||||
|
||||
desc('Run messenger consume');
|
||||
task('messenger:consume', function () {
|
||||
run("sudo supervisorctl restart messenger-consume:*");
|
||||
});
|
||||
|
||||
host('mangarr.test.nestor-server.fr')
|
||||
->set('remote_user', 'colgora')
|
||||
->set('deploy_path', '/var/www/mangarr')
|
||||
->set('branch', 'main');
|
||||
|
||||
// Hooks
|
||||
after('deploy:vendors', 'npm:install');
|
||||
after('npm:install', 'webpack_encore:build');
|
||||
after('deploy:vendors', 'database:migrate');
|
||||
after('deploy:symlink', 'messenger:consume');
|
||||
after('deploy:failed', 'deploy:unlock');
|
||||
@@ -16,9 +16,9 @@
|
||||
</properties>
|
||||
|
||||
<rule ref="rulesets/codesize.xml"/>
|
||||
<rule ref="rulesets/cleancode.xml"/>
|
||||
<rule ref="rulesets/controversial.xml"/>
|
||||
<rule ref="rulesets/design.xml"/>
|
||||
<!-- <rule ref="rulesets/cleancode.xml"/>-->
|
||||
<!-- <rule ref="rulesets/controversial.xml"/>-->
|
||||
<!-- <rule ref="rulesets/design.xml"/>-->
|
||||
<rule ref="rulesets/naming.xml"/>
|
||||
<rule ref="rulesets/unusedcode.xml"/>
|
||||
</ruleset>
|
||||
|
||||
@@ -203,6 +203,19 @@ class MangaController extends AbstractController
|
||||
return new JsonResponse(['success' => 'CBZ file deleted.'], 200);
|
||||
}
|
||||
|
||||
#[Route('/chapter/{id}/edit', name: 'app_chapter_edit', methods: ['POST'])]
|
||||
public function editChapter(Request $request, Chapter $chapter): JsonResponse
|
||||
{
|
||||
$data = json_decode($request->getContent(), true);
|
||||
|
||||
$chapter->setNumber($data['number']);
|
||||
$chapter->setTitle($data['title']);
|
||||
|
||||
$this->entityManager->flush();
|
||||
|
||||
return new JsonResponse(['success' => true, 'message' => 'Chapter updated successfully']);
|
||||
}
|
||||
|
||||
#[Route('/hide_chapter/{id}', name: 'app_hide_chapter')]
|
||||
public function hideChapter(Chapter $chapter): JsonResponse
|
||||
{
|
||||
@@ -393,7 +406,7 @@ class MangaController extends AbstractController
|
||||
'manga' => $manga,
|
||||
'volume' => $volume,
|
||||
'visible' => true
|
||||
]);
|
||||
], ['number' => 'ASC']);
|
||||
|
||||
if (empty($volumeChapters)) {
|
||||
$this->notificationService->sendUpdate(['status' => 'error', 'message' => 'Aucun chapitre trouvé pour ce volume.']);
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Controller;
|
||||
|
||||
use App\Repository\MangaRepository;
|
||||
use App\Service\CbzService;
|
||||
use App\Service\NotificationService;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
@@ -13,7 +14,8 @@ class ReaderController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly MangaRepository $mangaRepository,
|
||||
private readonly CbzService $cbzService
|
||||
private readonly CbzService $cbzService,
|
||||
private readonly NotificationService $notificationService,
|
||||
)
|
||||
{
|
||||
}
|
||||
@@ -32,7 +34,11 @@ class ReaderController extends AbstractController
|
||||
}
|
||||
|
||||
if (is_null($chapter->getCbzPath())) {
|
||||
throw $this->createNotFoundException("Le chapitre demandé n'a pas été scrapé.");
|
||||
$this->notificationService->sendUpdate([
|
||||
'status' => 'error',
|
||||
'message' => 'Le chapitre demandé n\'est pas encore disponible.',
|
||||
]);
|
||||
return $this->redirectToRoute('app_manga_show', ['mangaSlug' => $mangaSlug]);
|
||||
}
|
||||
|
||||
$totalPages = $this->cbzService->getPageCount($chapter->getCbzPath());
|
||||
@@ -81,10 +87,9 @@ class ReaderController extends AbstractController
|
||||
|
||||
$chapters = array_values(array_map(fn($chapter) => [
|
||||
'number' => $chapter->getNumber(),
|
||||
'title' => $chapter->getTitle()
|
||||
'title' => $chapter->getTitle(),
|
||||
], $chapters));
|
||||
|
||||
|
||||
return $this->json($chapters);
|
||||
}
|
||||
|
||||
@@ -96,13 +101,17 @@ class ReaderController extends AbstractController
|
||||
throw $this->createNotFoundException("Le manga demandé n'existe pas.");
|
||||
}
|
||||
|
||||
$previousChapter = $manga->getChapters()
|
||||
$chapters = $manga->getChapters()
|
||||
->filter(fn($chapter) => $chapter->isVisible() && $chapter->getNumber() < $currentChapterNumber)
|
||||
->last();
|
||||
->toArray();
|
||||
|
||||
usort($chapters, fn($a, $b) => $b->getNumber() <=> $a->getNumber());
|
||||
|
||||
$previousChapter = reset($chapters) ?: null;
|
||||
|
||||
return $this->json($previousChapter ? [
|
||||
'number' => $previousChapter->getNumber(),
|
||||
'title' => $previousChapter->getTitle()
|
||||
'title' => $previousChapter->getTitle(),
|
||||
] : null);
|
||||
}
|
||||
|
||||
@@ -124,7 +133,7 @@ class ReaderController extends AbstractController
|
||||
|
||||
return $this->json($nextChapter ? [
|
||||
'number' => $nextChapter->getNumber(),
|
||||
'title' => $nextChapter->getTitle()
|
||||
'title' => $nextChapter->getTitle(),
|
||||
] : null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,7 @@ class FileSystemManager
|
||||
private readonly Filesystem $filesystem,
|
||||
private readonly SluggerInterface $slugger,
|
||||
private readonly AppSettingsManager $appSettingsManager
|
||||
)
|
||||
{
|
||||
) {
|
||||
$this->loadSettings();
|
||||
}
|
||||
|
||||
@@ -46,14 +45,16 @@ class FileSystemManager
|
||||
if (!$this->filesystem->exists($this->projectDir.'/'.self::IMAGES_DIRECTORY.($subDir ? "/$subDir" : ''))) {
|
||||
$this->filesystem->mkdir($this->projectDir.'/'.self::IMAGES_DIRECTORY.($subDir ? "/$subDir" : ''), 0755);
|
||||
}
|
||||
|
||||
return $this->projectDir.'/'.self::IMAGES_DIRECTORY.($subDir ? "/$subDir" : '');
|
||||
}
|
||||
|
||||
public function createMangaDirectory(string $mangaSlug, ?int $year): string
|
||||
{
|
||||
$year = $year ?? 'unknown';
|
||||
$directoryPath = $this->mangaDirectory . '/' . ucfirst($mangaSlug) . " ($year)";
|
||||
$directoryPath = $this->projectDir.'/'.self::CBZ_DIRECTORY.'/'.ucfirst($mangaSlug)." ($year)";
|
||||
$this->filesystem->mkdir($directoryPath, 0755);
|
||||
|
||||
return $directoryPath;
|
||||
}
|
||||
|
||||
@@ -61,6 +62,7 @@ class FileSystemManager
|
||||
{
|
||||
$volumeDir = sprintf('%s/volume_%02d', $mangaDir, $volume);
|
||||
$this->filesystem->mkdir($volumeDir, 0755);
|
||||
|
||||
return $volumeDir;
|
||||
}
|
||||
|
||||
@@ -69,6 +71,7 @@ class FileSystemManager
|
||||
$newFilename = $this->generateUniqueFilename($originalFilename);
|
||||
$destinationPath = $destinationDir.'/'.$newFilename;
|
||||
$this->filesystem->rename($sourcePath, $destinationPath, true);
|
||||
|
||||
return $destinationPath;
|
||||
}
|
||||
|
||||
@@ -104,12 +107,14 @@ class FileSystemManager
|
||||
private function generateUniqueFilename(string $originalFilename): string
|
||||
{
|
||||
$safeFilename = $this->slugger->slug(pathinfo($originalFilename, PATHINFO_FILENAME));
|
||||
|
||||
return $safeFilename.'-'.uniqid().'.'.pathinfo($originalFilename, PATHINFO_EXTENSION);
|
||||
}
|
||||
|
||||
public function generateUniqueImageFilename(string $originalFilename): string
|
||||
{
|
||||
$safeFilename = $this->slugger->slug(pathinfo($originalFilename, PATHINFO_FILENAME));
|
||||
|
||||
return $safeFilename.'-'.uniqid().'.jpg';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ use App\Repository\ChapterRepository;
|
||||
use App\Repository\ContentSourceRepository;
|
||||
use App\Service\NotificationService;
|
||||
use App\Service\Scraper\MangaScraperService;
|
||||
use Exception;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
|
||||
@@ -21,13 +20,11 @@ readonly class DownloadChapterHandler
|
||||
private MangaScraperService $mangaScraperService,
|
||||
private NotificationService $notificationService,
|
||||
private ContentSourceRepository $contentSourceRepository
|
||||
)
|
||||
{
|
||||
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __invoke(DownloadChapter $message): void
|
||||
{
|
||||
@@ -35,7 +32,7 @@ readonly class DownloadChapterHandler
|
||||
if (!$chapter) {
|
||||
$this->notificationService->sendUpdate(['status' => 'error', 'message' => 'Chapter not found.']);
|
||||
throw new BadRequestHttpException('Chapter not found');
|
||||
} elseif ($chapter->getCbzPath() !== null) {
|
||||
} elseif (null !== $chapter->getCbzPath()) {
|
||||
$this->notificationService->sendUpdate(['status' => 'error', 'message' => 'Chapter already scraped.']);
|
||||
throw new BadRequestHttpException('Chapter already downloaded');
|
||||
}
|
||||
@@ -44,11 +41,17 @@ readonly class DownloadChapterHandler
|
||||
$preferredSources = $manga->getPreferredSources()->toArray();
|
||||
$allSources = $this->contentSourceRepository->findAll();
|
||||
|
||||
$filteredSources = array_udiff($allSources, $preferredSources, function ($a, $b) {
|
||||
return $a->getId() - $b->getId();
|
||||
});
|
||||
// $filteredSources = array_udiff($allSources, $preferredSources, function ($a, $b) {
|
||||
// return $a->getId() - $b->getId();
|
||||
// });
|
||||
//
|
||||
// $sources = array_merge($preferredSources, $filteredSources);
|
||||
|
||||
$sources = array_merge($preferredSources, $filteredSources);
|
||||
if (count($preferredSources) > 0) {
|
||||
$sources = $preferredSources;
|
||||
} else {
|
||||
$sources = $allSources;
|
||||
}
|
||||
|
||||
$sources[] =
|
||||
(new ContentSource())
|
||||
@@ -57,7 +60,6 @@ readonly class DownloadChapterHandler
|
||||
->setChapterUrlFormat('at-home/server/%s')
|
||||
->setScrapingType('mangadex');
|
||||
|
||||
|
||||
// (new ContentSource())
|
||||
// ->setBaseUrl('https://lelscans.net')
|
||||
// ->setImageSelector('#image img')
|
||||
@@ -78,10 +80,10 @@ readonly class DownloadChapterHandler
|
||||
$this->mangaScraperService->scrapeChapter($chapter, $source);
|
||||
$scrapedSuccessfully = true;
|
||||
break;
|
||||
} catch (Exception $e) {
|
||||
} catch (\Exception $e) {
|
||||
$this->notificationService->sendUpdate([
|
||||
'status' => 'warning',
|
||||
'message' => 'An error occurred while scraping with source: ' . $source->getBaseUrl() . '. Trying next source...'
|
||||
'message' => 'An error occurred while scraping with source: '.$source->getBaseUrl().'. Trying next source...',
|
||||
]);
|
||||
} catch (GuzzleException $e) {
|
||||
|
||||
@@ -91,9 +93,9 @@ readonly class DownloadChapterHandler
|
||||
if (!$scrapedSuccessfully) {
|
||||
$this->notificationService->sendUpdate([
|
||||
'status' => 'error',
|
||||
'message' => 'All sources failed to scrape the chapter ' . $chapter->getManga()->getTitle() . ' ' . $chapter->getNumber() . '.'
|
||||
'message' => 'All sources failed to scrape the chapter '.$chapter->getManga()->getTitle().' '.$chapter->getNumber().'.',
|
||||
]);
|
||||
throw new Exception('All sources failed to scrape the chapter ' . $chapter->getManga()->getTitle() . ' ' . $chapter->getNumber() . '.');
|
||||
throw new \Exception('All sources failed to scrape the chapter '.$chapter->getManga()->getTitle().' '.$chapter->getNumber().'.');
|
||||
}
|
||||
|
||||
$this->notificationService->sendUpdate(['status' => 'success', 'message' => 'Chapter scraped successfully.']);
|
||||
|
||||
@@ -3,38 +3,35 @@
|
||||
namespace App\Service;
|
||||
|
||||
use App\Entity\Manga;
|
||||
use Exception;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
||||
use Symfony\Component\String\Slugger\SluggerInterface;
|
||||
use ZipArchive;
|
||||
|
||||
class CbzService
|
||||
{
|
||||
|
||||
public function __construct(private SluggerInterface $slugger)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function extractMetadata(string $filePath, string $originalFileName): array
|
||||
{
|
||||
$zip = new ZipArchive();
|
||||
$zip = new \ZipArchive();
|
||||
|
||||
$fileInfo = $this->extractInfoFromFileName($originalFileName);
|
||||
|
||||
$metadata['title'] = $fileInfo['title'];
|
||||
$metadata['volume'] = $fileInfo['volume'] !== null ? (int)$fileInfo['volume'] : null;
|
||||
$metadata['chapter'] = $fileInfo['chapter'] !== null ? (int)$fileInfo['chapter'] : null;
|
||||
$metadata['volume'] = null !== $fileInfo['volume'] ? (int) $fileInfo['volume'] : null;
|
||||
$metadata['chapter'] = null !== $fileInfo['chapter'] ? (int) $fileInfo['chapter'] : null;
|
||||
|
||||
if (is_null($metadata['chapter'])) {
|
||||
try {
|
||||
$zip->open($filePath);
|
||||
$chapterNumbers = [];
|
||||
|
||||
for ($i = 0; $i < $zip->numFiles; $i++) {
|
||||
for ($i = 0; $i < $zip->numFiles; ++$i) {
|
||||
$stat = $zip->statIndex($i);
|
||||
$fileName = $stat['name'];
|
||||
|
||||
@@ -43,15 +40,15 @@ class CbzService
|
||||
|
||||
$chapterNumbers = array_unique($chapterNumbers);
|
||||
|
||||
if (count($chapterNumbers) === 1) {
|
||||
$metadata['chapter'] = array_values($chapterNumbers)[0] === '' ? null : (int)array_values($chapterNumbers)[0];
|
||||
if (1 === count($chapterNumbers)) {
|
||||
$metadata['chapter'] = '' === array_values($chapterNumbers)[0] ? null : (int) array_values($chapterNumbers)[0];
|
||||
} elseif (count($chapterNumbers) > 1) {
|
||||
$metadata['chapter'] = min($chapterNumbers);
|
||||
}
|
||||
|
||||
$zip->close();
|
||||
} catch (Exception $e) {
|
||||
throw new Exception("Impossible d'ouvrir le fichier CBZ. " . $e->getMessage());
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception("Impossible d'ouvrir le fichier CBZ. ".$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,27 +57,31 @@ class CbzService
|
||||
|
||||
public function getPageContent(string $cbzPath, int $pageNumber): ?string
|
||||
{
|
||||
$zip = new ZipArchive();
|
||||
if ($zip->open($cbzPath) === TRUE) {
|
||||
$zip = new \ZipArchive();
|
||||
if (true === $zip->open($cbzPath)) {
|
||||
$images = $this->getImageList($zip);
|
||||
if (isset($images[$pageNumber - 1])) {
|
||||
$content = $zip->getFromName($images[$pageNumber - 1]);
|
||||
$zip->close();
|
||||
|
||||
return $content;
|
||||
}
|
||||
$zip->close();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getPageCount(string $cbzPath): int
|
||||
{
|
||||
$zip = new ZipArchive();
|
||||
if ($zip->open($cbzPath) === TRUE) {
|
||||
$zip = new \ZipArchive();
|
||||
if (true === $zip->open($cbzPath)) {
|
||||
$count = count($this->getImageList($zip));
|
||||
$zip->close();
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -91,9 +92,9 @@ class CbzService
|
||||
$chapter = $this->extractChapter($fileName);
|
||||
|
||||
return [
|
||||
'title' => $title === '' ? null : $title,
|
||||
'volume' => $volume === '' ? null : $volume,
|
||||
'chapter' => $chapter === '' ? null : $chapter,
|
||||
'title' => '' === $title ? null : $title,
|
||||
'volume' => '' === $volume ? null : $volume,
|
||||
'chapter' => '' === $chapter ? null : $chapter,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -118,6 +119,7 @@ class CbzService
|
||||
if (preg_match($volumePattern, $fileName, $matches)) {
|
||||
return str_pad($matches['volume'], 2, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -136,52 +138,54 @@ class CbzService
|
||||
return '';
|
||||
}
|
||||
|
||||
private function getImageList(ZipArchive $zip): array
|
||||
private function getImageList(\ZipArchive $zip): array
|
||||
{
|
||||
$images = [];
|
||||
for ($i = 0; $i < $zip->numFiles; $i++) {
|
||||
for ($i = 0; $i < $zip->numFiles; ++$i) {
|
||||
$filename = $zip->getNameIndex($i);
|
||||
if (preg_match('/\.(jpg|jpeg|png|gif)$/i', $filename)) {
|
||||
$images[] = $filename;
|
||||
}
|
||||
}
|
||||
sort($images);
|
||||
|
||||
return $images;
|
||||
}
|
||||
|
||||
public function createVolumeArchive(array $chapters): string
|
||||
{
|
||||
$tempFile = tempnam(sys_get_temp_dir(), 'volume_cbz_');
|
||||
$zip = new ZipArchive();
|
||||
if ($zip->open($tempFile, ZipArchive::CREATE) !== TRUE) {
|
||||
throw new \RuntimeException("Impossible de créer le fichier ZIP temporaire.");
|
||||
$zip = new \ZipArchive();
|
||||
if (true !== $zip->open($tempFile, \ZipArchive::CREATE)) {
|
||||
throw new \RuntimeException('Impossible de créer le fichier ZIP temporaire.');
|
||||
}
|
||||
|
||||
foreach ($chapters as $chapter) {
|
||||
$chapterZip = new ZipArchive();
|
||||
if ($chapterZip->open($chapter->getCbzPath()) === TRUE) {
|
||||
for ($i = 0; $i < $chapterZip->numFiles; $i++) {
|
||||
$chapterZip = new \ZipArchive();
|
||||
if (true === $chapterZip->open($chapter->getCbzPath())) {
|
||||
for ($i = 0; $i < $chapterZip->numFiles; ++$i) {
|
||||
$filename = $chapterZip->getNameIndex($i);
|
||||
$fileContent = $chapterZip->getFromIndex($i);
|
||||
$zip->addFromString("Chapter " . $chapter->getNumber() . "/" . $filename, $fileContent);
|
||||
$zip->addFromString('Chapter '.$chapter->getNumber().'/'.$filename, $fileContent);
|
||||
}
|
||||
$chapterZip->close();
|
||||
}
|
||||
}
|
||||
|
||||
$zip->close();
|
||||
|
||||
return $tempFile;
|
||||
}
|
||||
|
||||
public function generateFileName(Manga $manga, ?int $volume = null, ?float $chapterNumber = null): string
|
||||
public function generateFileName(Manga $manga, int $volume = null, float $chapterNumber = null): string
|
||||
{
|
||||
$sluggedTitle = $this->slugger->slug($manga->getTitle())->lower();
|
||||
if ($volume !== null) {
|
||||
return sprintf("%s_volume_%02d.cbz", $sluggedTitle, $volume);
|
||||
} elseif ($chapterNumber !== null) {
|
||||
return sprintf("%s_chapter_%s.cbz", $sluggedTitle, number_format($chapterNumber, 2));
|
||||
if (null !== $volume) {
|
||||
return sprintf('%s_volume_%02d.cbz', $sluggedTitle, $volume);
|
||||
} elseif (null !== $chapterNumber) {
|
||||
return sprintf('%s_chapter_%s.cbz', $sluggedTitle, number_format($chapterNumber, 2));
|
||||
} else {
|
||||
throw new \InvalidArgumentException("Either volume or chapter number must be provided");
|
||||
throw new \InvalidArgumentException('Either volume or chapter number must be provided');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,6 +196,7 @@ class CbzService
|
||||
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
|
||||
$fileName
|
||||
);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
@@ -201,6 +206,7 @@ class CbzService
|
||||
return false;
|
||||
}
|
||||
$firstCbzPath = $chapters[0]->getCbzPath();
|
||||
|
||||
return array_reduce($chapters, function ($carry, $chapter) use ($firstCbzPath) {
|
||||
return $carry && $chapter->getCbzPath() === $firstCbzPath;
|
||||
}, true);
|
||||
@@ -209,7 +215,7 @@ class CbzService
|
||||
public function doAllChaptersHaveCbz(array $chapters): bool
|
||||
{
|
||||
return array_reduce($chapters, function ($carry, $chapter) {
|
||||
return $carry && $chapter->getCbzPath() !== null;
|
||||
return $carry && null !== $chapter->getCbzPath();
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,19 +18,20 @@ readonly class MangadexProvider implements MetadataProviderInterface
|
||||
|
||||
public function search(?string $title): Collection
|
||||
{
|
||||
if ($title === null) {
|
||||
if (null === $title) {
|
||||
return new ArrayCollection();
|
||||
}
|
||||
|
||||
try {
|
||||
$results = $this->client->get('/manga', [
|
||||
'title' => $title,
|
||||
'contentRating' => ['safe', 'suggestive'],
|
||||
'contentRating' => ['safe', 'suggestive', 'erotica'],
|
||||
'includes' => ['cover_art', 'author'],
|
||||
'limit' => 25
|
||||
'limit' => 50,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
$this->notificationService->sendUpdate('notification', ['status' => 'error', 'message' => 'An error occurred while fetching data from Mangadex.']);
|
||||
|
||||
return new ArrayCollection();
|
||||
}
|
||||
|
||||
@@ -51,11 +52,11 @@ readonly class MangadexProvider implements MetadataProviderInterface
|
||||
$mangas[count($mangas) - 1]->setGenres($tags);
|
||||
|
||||
foreach ($result['relationships'] as $relationship) {
|
||||
if ($relationship['type'] === 'author') {
|
||||
if ('author' === $relationship['type']) {
|
||||
$mangas[count($mangas) - 1]->setAuthor($relationship['attributes']['name']);
|
||||
}
|
||||
|
||||
if ($relationship['type'] === 'cover_art') {
|
||||
if ('cover_art' === $relationship['type']) {
|
||||
$mangas[count($mangas) - 1]->setImageUrl('https://mangadex.org/covers/'.$result['id'].'/'.$relationship['attributes']['fileName']);
|
||||
}
|
||||
}
|
||||
@@ -64,19 +65,21 @@ readonly class MangadexProvider implements MetadataProviderInterface
|
||||
$test = array_map(fn ($manga) => $manga->getExternalId(), $mangas);
|
||||
|
||||
$ratings = $this->client->get('/statistics/manga', [
|
||||
'manga' => $test
|
||||
'manga' => $test,
|
||||
]);
|
||||
|
||||
foreach ($mangas as $manga) {
|
||||
$manga->setRating($ratings['statistics'][$manga->getExternalId()]['rating']['average']);
|
||||
}
|
||||
|
||||
usort($mangas, fn ($a, $b) => $b->getRating() <=> $a->getRating());
|
||||
|
||||
return new ArrayCollection($mangas);
|
||||
}
|
||||
|
||||
public function getFeed(Manga $manga): array
|
||||
{
|
||||
if ($manga->getExternalId() === null) {
|
||||
if (null === $manga->getExternalId()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -90,7 +93,7 @@ readonly class MangadexProvider implements MetadataProviderInterface
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
$page++;
|
||||
++$page;
|
||||
} while (count($chapters) < $results['total']);
|
||||
|
||||
return $this->getChaptersFromFeed($chapters, $manga);
|
||||
@@ -98,7 +101,7 @@ readonly class MangadexProvider implements MetadataProviderInterface
|
||||
|
||||
public function getLastFeed(Manga $manga, int $limit = 100): array
|
||||
{
|
||||
if ($manga->getExternalId() === null) {
|
||||
if (null === $manga->getExternalId()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -111,6 +114,7 @@ readonly class MangadexProvider implements MetadataProviderInterface
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->notificationService->sendUpdate(['status' => 'error', 'message' => 'An error occurred while fetching recent chapters from Mangadex.']);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -124,10 +128,11 @@ readonly class MangadexProvider implements MetadataProviderInterface
|
||||
'limit' => $limit,
|
||||
'translatedLanguage' => ['en', 'fr'],
|
||||
'order' => ['chapter' => $order],
|
||||
'offset' => $page * $limit
|
||||
'offset' => $page * $limit,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
$this->notificationService->sendUpdate(['status' => 'error', 'message' => 'An error occurred while fetching data from Mangadex.']);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -136,7 +141,7 @@ readonly class MangadexProvider implements MetadataProviderInterface
|
||||
|
||||
public function getMangaAggregate(Manga $manga): array
|
||||
{
|
||||
if ($manga->getExternalId() === null) {
|
||||
if (null === $manga->getExternalId()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -148,9 +153,9 @@ readonly class MangadexProvider implements MetadataProviderInterface
|
||||
}
|
||||
|
||||
$chapterEntities = [];
|
||||
if ($response['result'] === 'ok') {
|
||||
if ('ok' === $response['result']) {
|
||||
foreach ($response['volumes'] as $volume) {
|
||||
$volumeNumber = $volume['volume'] === 'none' ? 0 : (float)$volume['volume'];
|
||||
$volumeNumber = 'none' === $volume['volume'] ? 0 : (float) $volume['volume'];
|
||||
foreach ($volume['chapters'] as $chapter) {
|
||||
$chapterEntity = new Chapter();
|
||||
$chapterEntity->setNumber((float) $chapter['chapter'])
|
||||
@@ -163,15 +168,10 @@ readonly class MangadexProvider implements MetadataProviderInterface
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $chapterEntities;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $chapters
|
||||
* @param Manga $manga
|
||||
* @param array $chapterEntities
|
||||
* @return array
|
||||
*/
|
||||
public function getChaptersFromFeed(mixed $chapters, Manga $manga): array
|
||||
{
|
||||
$chapterEntities = [];
|
||||
@@ -219,8 +219,9 @@ readonly class MangadexProvider implements MetadataProviderInterface
|
||||
if (empty($allChapters)) {
|
||||
$this->notificationService->sendUpdate([
|
||||
'status' => 'error',
|
||||
'message' => 'No chapters found for this manga.'
|
||||
'message' => 'No chapters found for this manga.',
|
||||
]);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -247,6 +248,5 @@ readonly class MangadexProvider implements MetadataProviderInterface
|
||||
{
|
||||
$existingChapter->setVolume($newChapter->getVolume());
|
||||
$existingChapter->setExternalId($newChapter->getExternalId());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ use App\Entity\Manga;
|
||||
use App\Event\PageScrappingProgressEvent;
|
||||
use App\Manager\FileSystemManager;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Exception;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
@@ -22,8 +21,7 @@ abstract class AbstractScraper implements ScraperInterface
|
||||
protected FileSystemManager $fileSystemManager,
|
||||
protected EventDispatcherInterface $eventDispatcher,
|
||||
protected EntityManagerInterface $entityManager
|
||||
)
|
||||
{
|
||||
) {
|
||||
$this->httpClient = new Client();
|
||||
}
|
||||
|
||||
@@ -45,7 +43,8 @@ abstract class AbstractScraper implements ScraperInterface
|
||||
{
|
||||
try {
|
||||
$response = $this->httpClient->head($url);
|
||||
return $response->getStatusCode() === 200;
|
||||
|
||||
return 200 === $response->getStatusCode();
|
||||
} catch (RequestException $e) {
|
||||
return false;
|
||||
}
|
||||
@@ -60,6 +59,7 @@ abstract class AbstractScraper implements ScraperInterface
|
||||
$chapter->getVolume(),
|
||||
$chapter->getNumber()
|
||||
);
|
||||
|
||||
return $volumeDir.'/'.$fileName;
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ abstract class AbstractScraper implements ScraperInterface
|
||||
{
|
||||
$zip = new \ZipArchive();
|
||||
|
||||
if ($zip->open($cbzFilePath, \ZipArchive::CREATE) === TRUE) {
|
||||
if (true === $zip->open($cbzFilePath, \ZipArchive::CREATE)) {
|
||||
foreach ($pageData as $page) {
|
||||
$zip->addFile($page['local_image_url'], basename($page['local_image_url']));
|
||||
}
|
||||
@@ -93,21 +93,67 @@ abstract class AbstractScraper implements ScraperInterface
|
||||
|
||||
/**
|
||||
* @throws GuzzleException
|
||||
* @throws Exception
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function downloadAndSaveImage(string $imageUrl, string $destinationPath): void
|
||||
protected function downloadAndSaveImage(string $imageUrl, string $destinationPath): string
|
||||
{
|
||||
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);
|
||||
if (!str_starts_with($contentType, 'image/')) {
|
||||
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());
|
||||
|
||||
$imageData = $response->getBody()->getContents();
|
||||
$tempFilePath = $this->saveTempFile($imageData);
|
||||
|
||||
$image = $this->createImageResource($tempFilePath, $contentType);
|
||||
if (false === $image) {
|
||||
throw new \Exception('Échec de la création de la ressource image.');
|
||||
}
|
||||
|
||||
$destinationPath = $this->ensureJpgExtension($destinationPath);
|
||||
if (!imagejpeg($image, $destinationPath)) {
|
||||
imagedestroy($image);
|
||||
unlink($tempFilePath);
|
||||
throw new \Exception('Échec de la sauvegarde de l\'image en JPG.');
|
||||
}
|
||||
|
||||
imagedestroy($image);
|
||||
unlink($tempFilePath);
|
||||
|
||||
return $destinationPath;
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception('Erreur lors de la récupération de l\'image : '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private function saveTempFile(string $data): string
|
||||
{
|
||||
$tempFilePath = tempnam(sys_get_temp_dir(), 'manga_img_');
|
||||
file_put_contents($tempFilePath, $data);
|
||||
|
||||
return $tempFilePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function createImageResource(string $filePath, string $contentType)
|
||||
{
|
||||
return match ($contentType) {
|
||||
'image/webp' => imagecreatefromwebp($filePath),
|
||||
'image/png' => imagecreatefrompng($filePath),
|
||||
'image/jpeg', 'image/jpg' => imagecreatefromjpeg($filePath),
|
||||
default => throw new \Exception('Format d\'image non pris en charge : '.$contentType),
|
||||
};
|
||||
}
|
||||
|
||||
private function ensureJpgExtension(string $path): string
|
||||
{
|
||||
$info = pathinfo($path);
|
||||
|
||||
return $info['dirname'].'/'.$info['filename'].'.jpg';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,17 +4,13 @@ namespace App\Service\Scraper;
|
||||
|
||||
use App\Entity\Chapter;
|
||||
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;
|
||||
|
||||
class HtmlScraper extends AbstractScraper
|
||||
{
|
||||
/**
|
||||
* @throws Exception
|
||||
* @throws \Exception
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function scrapeChapter(Chapter $chapter, ContentSource $contentSource): array|bool
|
||||
@@ -23,7 +19,7 @@ class HtmlScraper extends AbstractScraper
|
||||
$chapterUrl = $this->getValidChapterUrl($contentSource, $manga, $chapter->getNumber());
|
||||
|
||||
if (!$chapterUrl) {
|
||||
throw new Exception("Aucune URL valide trouvée pour le chapitre {$chapter->getNumber()} du manga {$manga->getTitle()}");
|
||||
throw new \Exception("Aucune URL valide trouvée pour le chapitre {$chapter->getNumber()} du manga {$manga->getTitle()}");
|
||||
}
|
||||
|
||||
$tempDir = sys_get_temp_dir().'/'.uniqid('manga_scraper_');
|
||||
@@ -31,7 +27,7 @@ class HtmlScraper extends AbstractScraper
|
||||
|
||||
$pageData = [];
|
||||
|
||||
if ($contentSource->getNextPageSelector() === null) {
|
||||
if (null === $contentSource->getNextPageSelector()) {
|
||||
// Lecteur vertical
|
||||
$html = $this->fetchHtml($chapterUrl);
|
||||
$pageData = $this->scrapeVerticalReader($html, $contentSource);
|
||||
@@ -45,11 +41,11 @@ class HtmlScraper extends AbstractScraper
|
||||
$imageName = sprintf('%03d.%s', $index + 1, pathinfo(parse_url($page['image_url'], PHP_URL_PATH), PATHINFO_EXTENSION));
|
||||
$imagePath = $tempDir.'/'.$imageName;
|
||||
|
||||
$this->downloadAndSaveImage($page['image_url'], $imagePath);
|
||||
$destinationPath = $this->downloadAndSaveImage($page['image_url'], $imagePath);
|
||||
|
||||
$this->dispatchProgressEvent($chapter, $index + 1, count($pageData));
|
||||
|
||||
$page['local_image_url'] = $imagePath;
|
||||
$page['local_image_url'] = $destinationPath;
|
||||
}
|
||||
|
||||
$cbzFilePath = $this->generateCbzPath($manga, $chapter);
|
||||
@@ -59,26 +55,25 @@ class HtmlScraper extends AbstractScraper
|
||||
$this->entityManager->persist($chapter);
|
||||
$this->entityManager->flush();
|
||||
|
||||
// Nettoyage du répertoire temporaire
|
||||
$this->cleanupTempFiles($tempDir);
|
||||
|
||||
return $pageData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function testScraping(string $mangaSlug, string $chapterNumber, ContentSource $contentSource): array
|
||||
{
|
||||
$chapterUrl = $contentSource->getChapterUrl($mangaSlug, $chapterNumber);
|
||||
|
||||
if (!$this->isChapterUrlValid($chapterUrl)) {
|
||||
throw new \Exception("Invalid URL, check format and slug");
|
||||
throw new \Exception('Invalid URL, check format and slug');
|
||||
}
|
||||
|
||||
$html = $this->fetchHtml($chapterUrl);
|
||||
|
||||
if ($contentSource->getNextPageSelector() === null) {
|
||||
if (null === $contentSource->getNextPageSelector()) {
|
||||
return $this->scrapeVerticalReader($html, $contentSource);
|
||||
} else {
|
||||
return $this->scrapeHorizontalReader($chapterUrl, $contentSource);
|
||||
@@ -87,7 +82,7 @@ class HtmlScraper extends AbstractScraper
|
||||
|
||||
public function supports(string $scrapingType): bool
|
||||
{
|
||||
return $scrapingType === 'html';
|
||||
return 'html' === $scrapingType;
|
||||
}
|
||||
|
||||
private function scrapeVerticalReader(string $html, ContentSource $contentSource): array
|
||||
@@ -108,7 +103,7 @@ class HtmlScraper extends AbstractScraper
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function scrapeHorizontalReader(string $chapterUrl, ContentSource $contentSource): array
|
||||
{
|
||||
@@ -135,18 +130,18 @@ class HtmlScraper extends AbstractScraper
|
||||
try {
|
||||
$response = $this->httpClient->get($url, [
|
||||
'http_errors' => true,
|
||||
'allow_redirects' => false
|
||||
'allow_redirects' => false,
|
||||
]);
|
||||
|
||||
$statusCode = $response->getStatusCode();
|
||||
|
||||
if ($statusCode >= 300 && $statusCode < 400 || $statusCode == 404) {
|
||||
throw new Exception('Chapter Not Found at ' . $url);
|
||||
if ($statusCode >= 300 && $statusCode < 400 || 404 == $statusCode) {
|
||||
throw new \Exception('Chapter Not Found at '.$url);
|
||||
}
|
||||
|
||||
return (string) $response->getBody();
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('Bad Request: ' . $e->getMessage());
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception('Bad Request: '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,10 +40,10 @@ class JavascriptScraper extends AbstractScraper
|
||||
$imageName = sprintf('%03d.%s', $index + 1, pathinfo(parse_url($page['image_url'], PHP_URL_PATH), PATHINFO_EXTENSION));
|
||||
$imagePath = $tempDir . '/' . $imageName;
|
||||
|
||||
$this->downloadAndSaveImage($page['image_url'], $imagePath);
|
||||
$destinationPath = $this->downloadAndSaveImage($page['image_url'], $imagePath);
|
||||
$this->dispatchProgressEvent($chapter, $index + 1, count($pageData));
|
||||
|
||||
$page['local_image_url'] = $imagePath;
|
||||
$page['local_image_url'] = $destinationPath;
|
||||
}
|
||||
|
||||
$cbzFilePath = $this->generateCbzPath($manga, $chapter);
|
||||
|
||||
Reference in New Issue
Block a user