chapterRepository->getById($command->chapterId); if (!$chapter) { throw new \InvalidArgumentException("Chapter not found with ID: {$command->chapterId}"); } // 2. Récupération du manga $manga = $this->mangaRepository->getById($chapter->mangaId); if (!$manga) { throw new \InvalidArgumentException("Manga not found with ID: {$chapter->mangaId}"); } // 3. Dispatch de l'événement de démarrage $this->eventBus->dispatch(new ChapterScrapingStarted($manga->getTitle(), $chapter->chapterNumber)); // 4. Détermination des sources à utiliser $sources = $this->getSourcesToTry($manga); if (empty($sources)) { throw new \InvalidArgumentException("No sources available for scraping"); } // 5. Essai de scraping sur chaque source jusqu'à succès $success = false; $lastException = null; foreach ($sources as $source) { // Préparer la liste des slugs à essayer : slug principal + slugs alternatifs $slugsToTry = array_merge([$manga->getSlug()], $manga->getAlternativeSlugs()); foreach ($slugsToTry as $slug) { $job = new ScrapingJob( Uuid::uuid4()->toString(), $chapter->mangaId, $chapter->chapterNumber, $source->getId()->getValue() ); // Ajout de l'ID du chapitre et du slug dans le contexte du job $job->context['chapterId'] = $command->chapterId; $job->context['slug'] = $slug; $job->context['mangaTitle'] = $manga->getTitle(); $job->start(); $this->jobRepository->save($job); try { $this->entityManager->beginTransaction(); // 5. Scraping des URLs avec le slug courant $scrapingParameters = $source->getScrappingParameters(); $scrapingParameters['chapterNumber'] = $chapter->chapterNumber; $scrapingType = $scrapingParameters['scrapingType'] ?? 'html'; $scrapingRequest = new ScrapingRequest( $scrapingType, $source->buildChapterUrl($slug, $chapter->chapterNumber), $scrapingParameters ); // Sélection du scraper approprié selon le type $scraper = $this->scraperFactory->getScraperWithFallback($scrapingType); $scrapingResult = $scraper->scrape($scrapingRequest); // 6. Téléchargement des images $tempDir = new TempDirectory(); $downloadResults = $this->imageDownloader->downloadBatch( $scrapingResult->getImageUrls(), $tempDir, $job->id ); // 7. Stockage des images individuelles $localPaths = array_map(fn ($r) => $r->getLocalPath(), $downloadResults); $pagesDirectory = $this->imageStorage->storeChapterImages($command->chapterId, $localPaths); $pageCount = count($downloadResults); $job->complete(); $this->jobRepository->save($job); $this->entityManager->commit(); $this->eventBus->dispatch(new ChapterScraped($job->id, $command->chapterId, $pagesDirectory, $pageCount)); // 8. Nettoyage $tempDir->cleanup(); // Scraping réussi, pas besoin d'essayer d'autres slugs ni d'autres sources $success = true; break; } catch (\Exception $e) { dump('EXCEPTION for source ' . $source->getName() . ' with slug ' . $slug . ': ' . $e->getMessage()); $this->entityManager->rollback(); if (isset($job)) { $job->fail($e->getMessage()); $this->jobRepository->save($job); } $lastException = $e; // Continuer avec le slug suivant pour cette source } } // Si le scraping a réussi avec un des slugs, sortir de la boucle des sources if ($success) { break; } } // Si toutes les sources ont échoué if (!$success) { $errorMessage = $lastException ? $lastException->getMessage() : "Failed to scrape chapter from all available sources"; $this->eventBus->dispatch(new ChapterScrapingFailed($chapter->mangaId, $chapter->chapterNumber, $errorMessage)); } } catch (\Exception $e) { if (isset($job)) { $job->fail($e->getMessage()); $this->jobRepository->save($job); } $this->eventBus->dispatch(new ChapterScrapingFailed($chapter->mangaId ?? 'unknown', $chapter->chapterNumber ?? 'unknown', $e->getMessage())); } } /** * Détermine les sources à utiliser pour le scraping en fonction des préférences du manga * * @param \App\Domain\Scraping\Domain\Model\Manga $manga * @return Source[] */ private function getSourcesToTry(\App\Domain\Scraping\Domain\Model\Manga $manga): array { // Si le manga a des sources préférées, les utiliser if ($manga->hasPreferredSources()) { $preferredSources = []; foreach ($manga->getPreferredSources() as $sourceId) { $source = $this->sourceRepository->getById($sourceId); if ($source) { $preferredSources[] = $source; } // Limiter à 3 sources préférées maximum if (count($preferredSources) >= 3) { break; } } if (!empty($preferredSources)) { return $preferredSources; } } // Sinon, utiliser toutes les sources disponibles return $this->sourceRepository->getAll(); } }