253 lines
8.8 KiB
PHP
253 lines
8.8 KiB
PHP
<?php
|
|
|
|
namespace App\Service;
|
|
|
|
use App\Entity\Chapter;
|
|
use App\Entity\Manga;
|
|
use App\Interface\ClientInterface;
|
|
use App\Interface\MetadataProviderInterface;
|
|
use Doctrine\Common\Collections\ArrayCollection;
|
|
use Doctrine\Common\Collections\Collection;
|
|
use Symfony\Component\String\Slugger\SluggerInterface;
|
|
|
|
readonly class MangadexProvider implements MetadataProviderInterface
|
|
{
|
|
public function __construct(private ClientInterface $client, private SluggerInterface $slugger, private NotificationService $notificationService)
|
|
{
|
|
}
|
|
|
|
public function search(?string $title): Collection
|
|
{
|
|
if (null === $title) {
|
|
return new ArrayCollection();
|
|
}
|
|
|
|
try {
|
|
$results = $this->client->get('/manga', [
|
|
'title' => $title,
|
|
'contentRating' => ['safe', 'suggestive', 'erotica'],
|
|
'includes' => ['cover_art', 'author'],
|
|
'limit' => 50,
|
|
]);
|
|
} catch (\Exception $e) {
|
|
$this->notificationService->sendUpdate('notification', ['status' => 'error', 'message' => 'An error occurred while fetching data from Mangadex.']);
|
|
|
|
return new ArrayCollection();
|
|
}
|
|
|
|
$mangas = [];
|
|
foreach ($results['data'] as $result) {
|
|
$mangas[] = (new Manga())
|
|
->setExternalId($result['id'])
|
|
->setTitle($result['attributes']['title']['en'])
|
|
->setSlug($this->slugger->slug($result['attributes']['title']['en'])->lower())
|
|
->setDescription($result['attributes']['description']['fr'] ?? $result['attributes']['description']['en'] ?? '')
|
|
->setPublicationYear($result['attributes']['year'])
|
|
->setStatus($result['attributes']['status']);
|
|
$tags = [];
|
|
foreach ($result['attributes']['tags'] as $tag) {
|
|
$tags[] = $tag['attributes']['name']['en'];
|
|
}
|
|
|
|
$mangas[count($mangas) - 1]->setGenres($tags);
|
|
|
|
foreach ($result['relationships'] as $relationship) {
|
|
if ('author' === $relationship['type']) {
|
|
$mangas[count($mangas) - 1]->setAuthor($relationship['attributes']['name']);
|
|
}
|
|
|
|
if ('cover_art' === $relationship['type']) {
|
|
$mangas[count($mangas) - 1]->setImageUrl('https://mangadex.org/covers/'.$result['id'].'/'.$relationship['attributes']['fileName']);
|
|
}
|
|
}
|
|
}
|
|
|
|
$test = array_map(fn ($manga) => $manga->getExternalId(), $mangas);
|
|
|
|
$ratings = $this->client->get('/statistics/manga', [
|
|
'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 (null === $manga->getExternalId()) {
|
|
return [];
|
|
}
|
|
|
|
$chapters = [];
|
|
$page = 0;
|
|
|
|
do {
|
|
$results = $this->getFeedWithPagination($manga->getExternalId(), $page);
|
|
if (isset($results['data'])) {
|
|
$chapters = array_merge($chapters, $results['data']);
|
|
} else {
|
|
break;
|
|
}
|
|
++$page;
|
|
} while (count($chapters) < $results['total']);
|
|
|
|
return $this->getChaptersFromFeed($chapters, $manga);
|
|
}
|
|
|
|
public function getLastFeed(Manga $manga, int $limit = 100): array
|
|
{
|
|
if (null === $manga->getExternalId()) {
|
|
return [];
|
|
}
|
|
|
|
$chapters = [];
|
|
|
|
try {
|
|
$results = $this->getFeedWithPagination($manga->getExternalId(), 0, $limit, 'desc');
|
|
if (isset($results['data'])) {
|
|
$chapters = $results['data'];
|
|
}
|
|
} catch (\Exception $e) {
|
|
$this->notificationService->sendUpdate(['status' => 'error', 'message' => 'An error occurred while fetching recent chapters from Mangadex.']);
|
|
|
|
return [];
|
|
}
|
|
|
|
return $this->getChaptersFromFeed($chapters, $manga);
|
|
}
|
|
|
|
private function getFeedWithPagination(string $externalId, int $page, int $limit = 500, string $order = 'asc'): array
|
|
{
|
|
try {
|
|
$response = $this->client->get('/manga/'.$externalId.'/feed', [
|
|
'limit' => $limit,
|
|
'translatedLanguage' => ['en', 'fr'],
|
|
'order' => ['chapter' => $order],
|
|
'offset' => $page * $limit,
|
|
]);
|
|
} catch (\Exception $e) {
|
|
$this->notificationService->sendUpdate(['status' => 'error', 'message' => 'An error occurred while fetching data from Mangadex.']);
|
|
|
|
return [];
|
|
}
|
|
|
|
return $response;
|
|
}
|
|
|
|
public function getMangaAggregate(Manga $manga): array
|
|
{
|
|
if (null === $manga->getExternalId()) {
|
|
return [];
|
|
}
|
|
|
|
try {
|
|
$response = $this->client->get('/manga/'.$manga->getExternalId().'/aggregate');
|
|
} catch (\Exception $e) {
|
|
// $this->notificationService->sendUpdate(['status' => 'error', 'message' => 'An error occurred while fetching data from Mangadex.']);
|
|
return [];
|
|
}
|
|
|
|
$chapterEntities = [];
|
|
if ('ok' === $response['result']) {
|
|
foreach ($response['volumes'] as $volume) {
|
|
$volumeNumber = 'none' === $volume['volume'] ? 0 : (float) $volume['volume'];
|
|
foreach ($volume['chapters'] as $chapter) {
|
|
$chapterEntity = new Chapter();
|
|
$chapterEntity->setNumber((float) $chapter['chapter'])
|
|
->setTitle('Chapter '.$chapter['chapter'])
|
|
->setVolume($volumeNumber)
|
|
->setExternalId('');
|
|
|
|
$chapterEntities[] = $chapterEntity;
|
|
// $manga->addChapter($chapterEntity);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $chapterEntities;
|
|
}
|
|
|
|
public function getChaptersFromFeed(mixed $chapters, Manga $manga): array
|
|
{
|
|
$chapterEntities = [];
|
|
$uniqueChapterNumbers = [];
|
|
|
|
foreach ($chapters as $result) {
|
|
$chapterNumber = (float) $result['attributes']['chapter'];
|
|
|
|
// Vérifiez si le chapitre existe déjà dans la base de données
|
|
$chapterExists = $manga->getChapters()->exists(function ($key, $existingChapter) use ($chapterNumber) {
|
|
return $existingChapter->getNumber() === $chapterNumber;
|
|
});
|
|
|
|
// Si le chapitre existe déjà dans la base de données ou dans notre nouvelle liste, on skip
|
|
if ($chapterExists || in_array($chapterNumber, $uniqueChapterNumbers)) {
|
|
continue;
|
|
}
|
|
|
|
// Créez et ajoutez le nouveau chapitre
|
|
$chapter = new Chapter();
|
|
$chapter->setNumber($chapterNumber)
|
|
->setTitle($result['attributes']['title'])
|
|
->setVolume((int) $result['attributes']['volume'] ?? null)
|
|
->setExternalId($result['id']);
|
|
|
|
$chapterEntities[] = $chapter;
|
|
$uniqueChapterNumbers[] = $chapterNumber;
|
|
}
|
|
|
|
// Trier les chapitres par numéro
|
|
usort($chapterEntities, function ($a, $b) {
|
|
return $a->getNumber() <=> $b->getNumber();
|
|
});
|
|
|
|
return $chapterEntities;
|
|
}
|
|
|
|
public function addAllChaptersToManga(Manga $manga): array
|
|
{
|
|
$mangaFeed = $this->getFeed($manga);
|
|
$mangaAggregate = $this->getMangaAggregate($manga);
|
|
|
|
$allChapters = array_merge($mangaFeed, $mangaAggregate);
|
|
|
|
if (empty($allChapters)) {
|
|
$this->notificationService->sendUpdate([
|
|
'status' => 'error',
|
|
'message' => 'No chapters found for this manga.',
|
|
]);
|
|
|
|
return [];
|
|
}
|
|
|
|
$mergedChapters = [];
|
|
foreach ($allChapters as $chapter) {
|
|
$number = $chapter->getNumber();
|
|
$existingChapter = $manga->getChapterByNumber($number);
|
|
if ($existingChapter) {
|
|
if ($existingChapter->getExternalId() !== $chapter->getExternalId() && is_null($existingChapter->getExternalId())) {
|
|
$this->updateChapter($existingChapter, $chapter);
|
|
$mergedChapters[$number] = $existingChapter;
|
|
}
|
|
} else {
|
|
// Add new chapter
|
|
$manga->addChapter($chapter);
|
|
$mergedChapters[$number] = $chapter;
|
|
}
|
|
}
|
|
|
|
return array_values($mergedChapters);
|
|
}
|
|
|
|
private function updateChapter(Chapter $existingChapter, Chapter $newChapter): void
|
|
{
|
|
$existingChapter->setVolume($newChapter->getVolume());
|
|
$existingChapter->setExternalId($newChapter->getExternalId());
|
|
}
|
|
}
|