feat: refonte du gestionnaire de chapitres pour intégrer la génération de fichiers CBZ, le téléchargement d'images en lot et la gestion des requêtes de scraping, avec mise à jour des interfaces et des modèles associés

This commit is contained in:
ext.jeremy.guillot@maxicoffee.domains
2025-03-28 20:42:21 +01:00
parent cdee6f77fc
commit d7088b14c2
22 changed files with 620 additions and 195 deletions

View File

@@ -4,13 +4,19 @@ namespace App\Domain\Scraping\Application\CommandHandler;
use App\Domain\Scraping\Application\Command\ScrapeChapter;
use App\Domain\Scraping\Domain\Contract\Repository\ChapterRepositoryInterface;
use App\Domain\Scraping\Domain\Contract\Repository\MangaRepositoryInterface;
use App\Domain\Scraping\Domain\Contract\Repository\ScrapingJobRepositoryInterface;
use App\Domain\Scraping\Domain\Contract\Repository\SourceRepositoryInterface;
use App\Domain\Scraping\Domain\Contract\Service\CbzGeneratorInterface;
use App\Domain\Scraping\Domain\Contract\Service\ImageDownloaderInterface;
use App\Domain\Scraping\Domain\Contract\Service\ScraperInterface;
use App\Domain\Scraping\Domain\Event\ChapterScraped;
use App\Domain\Scraping\Domain\Event\ChapterScrapingFailed;
use App\Domain\Scraping\Domain\Event\ChapterScrapingStarted;
use App\Domain\Scraping\Domain\Model\ScrapingJob;
use App\Domain\Scraping\Domain\Model\ScrapingStatus;
use App\Domain\Scraping\Domain\Model\ValueObject\CbzGenerationRequest;
use App\Domain\Scraping\Domain\Model\ValueObject\ScrapingRequest;
use App\Domain\Scraping\Domain\Model\ValueObject\TempDirectory;
use Ramsey\Uuid\Uuid;
use Symfony\Component\Messenger\MessageBusInterface;
@@ -18,8 +24,12 @@ readonly class ScrapeChapterHandler
{
public function __construct(
private ScraperInterface $scraper,
private ImageDownloaderInterface $imageDownloader,
private CbzGeneratorInterface $cbzGenerator,
private ScrapingJobRepositoryInterface $scrapingJobRepository,
private ChapterRepositoryInterface $chapterRepository,
private MangaRepositoryInterface $mangaRepository,
private SourceRepositoryInterface $sourceRepository,
private MessageBusInterface $eventBus
) {
}
@@ -27,30 +37,71 @@ readonly class ScrapeChapterHandler
public function handle(ScrapeChapter $command): void
{
try {
// 1. Création du job
$job = new ScrapingJob(
Uuid::uuid4(),
Uuid::uuid4()->toString(),
$command->mangaId,
$command->chapterNumber,
$command->sourceId
);
$this->scrapingJobRepository->save($job);
// 2. Préparation des données
$manga = $this->mangaRepository->getById($command->mangaId);
$chapter = $this->chapterRepository->getByMangaIdAndChapterNumber($command->mangaId, $command->chapterNumber);
$source = $this->sourceRepository->getById($command->sourceId);
$this->eventBus->dispatch(new ChapterScrapingStarted($job->getId()));
$job = $this->scraper->scrape($job);
if($job->status === ScrapingStatus::FAILED) {
$this->eventBus->dispatch(new ChapterScrapingFailed($command->mangaId, $command->chapterNumber, $job->failureReason));
}elseif ($job->status === ScrapingStatus::COMPLETED) {
$this->eventBus->dispatch(new ChapterScraped($job->getId()));
$chapter = $this->chapterRepository->getByMangaIdAndChapterNumber($command->mangaId, $command->chapterNumber);
$chapter->cbzPath = $job->cbzPath->getPath();
$this->chapterRepository->save($chapter);
}
// 3. Scraping des URLs
$scrapingRequest = new ScrapingRequest(
'html',
$source->buildChapterUrl($manga->getSlug(), $command->chapterNumber),
$source->getScrappingParameters(),
$job->getId()
);
$scrapingResult = $this->scraper->scrape($scrapingRequest);
$job->totalPages = $scrapingResult->getTotalPages();
$this->scrapingJobRepository->save($job);
// 4. Téléchargement des images
$tempDir = new TempDirectory();
$downloadResults = $this->imageDownloader->downloadBatch(
$scrapingResult->getImageUrls(),
$tempDir,
$job->getId()
);
// 5. Génération du CBZ
$cbzRequest = new CbzGenerationRequest(
$manga->getTitle(),
$manga->getPublicationYear(),
$chapter->volumeNumber,
$command->chapterNumber,
$tempDir,
array_map(fn($r) => $r->getLocalPath(), $downloadResults)
);
$cbzPath = $this->cbzGenerator->generate($cbzRequest);
// 6. Mise à jour et sauvegarde
$job->complete();
$job->cbzPath = $cbzPath;
$this->scrapingJobRepository->save($job);
$chapter->cbzPath = $cbzPath->getPath();
$this->chapterRepository->save($chapter);
$this->eventBus->dispatch(new ChapterScraped($job->getId()));
// 7. Nettoyage
$tempDir->cleanup();
} catch (\Exception $e) {
if (isset($job)) {
$job->fail($e->getMessage());
$this->scrapingJobRepository->save($job);
}
$this->eventBus->dispatch(new ChapterScrapingFailed($command->mangaId, $command->chapterNumber, $e->getMessage()));
throw $e;
}