Files
Mangarr/src/Domain/Reader/Infrastructure/Persistence/LegacyChapterRepository.php
ext.jeremy.guillot@maxicoffee.domains 2e3abb76c3 fix(import): extraire les images CBZ vers le stockage individuel
Corrige l'import de chapitres/volumes CBZ qui stockait le chemin du fichier
CBZ comme pagesDirectory. Le reader ne trouvait aucune image car
LegacyChapterRepository attend un dossier d'images individuelles.

- Déplace ImageStorageInterface dans Shared (storeChapterImages + extractFromCbz + countCbzImages)
- Crée ImageStorageManager dans Shared/Infrastructure (extraction ZIP + copie)
- Supprime LocalImageStorage et l'ancienne interface dans Scraping
- Refactore ImportChapterHandler et ImportVolumeHandler pour utiliser ImageStorageInterface
- Corrige LegacyChapterRepository : construit l'URL depuis basename(pagesDirectory)
  au lieu de chapterId (fix pour les volumes partagés)
2026-03-15 18:26:28 +01:00

166 lines
5.6 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Domain\Reader\Infrastructure\Persistence;
use App\Domain\Reader\Domain\Contract\Repository\ChapterRepositoryInterface;
use App\Domain\Reader\Domain\Exception\ChapterNotFoundException;
use App\Domain\Reader\Domain\Model\ChapterContext;
use App\Domain\Reader\Domain\Model\Page;
use App\Domain\Reader\Domain\ValueObject\ChapterId;
use App\Domain\Reader\Domain\ValueObject\PageNumber;
use App\Entity\Chapter as ChapterEntity;
use Doctrine\ORM\EntityManagerInterface;
readonly class LegacyChapterRepository implements ChapterRepositoryInterface
{
public function __construct(
private EntityManagerInterface $entityManager
) {
}
public function getPagesForChapter(ChapterId $chapterId, int $page = 1, int $itemsPerPage = 20): array
{
$chapter = $this->entityManager->getRepository(ChapterEntity::class)->findOneBy([
'id' => $chapterId->getValue()
]);
$pagesDirectory = $chapter->getPagesDirectory();
if ($pagesDirectory && is_dir($pagesDirectory)) {
return $this->getPagesFromDirectory($chapterId, $pagesDirectory, $page, $itemsPerPage);
}
return [];
}
public function getChapterContext(ChapterId $chapterId): ChapterContext
{
/** @var ChapterEntity $chapter */
$chapter = $this->entityManager->getRepository(ChapterEntity::class)->findOneBy([
'id' => $chapterId->getValue()
]);
if (!$chapter) {
throw ChapterNotFoundException::forChapter($chapterId);
}
return new ChapterContext(
id: $chapterId,
previousChapterId: $this->getPreviousChapterId($chapterId),
nextChapterId: $this->getNextChapterId($chapterId),
mangaId: (string) $chapter->getManga()->getId(),
mangaTitle: $chapter->getManga()->getTitle(),
number: $chapter->getNumber(),
chapterTitle: $chapter->getTitle(),
cbzPath: $chapter->getCbzPath(),
volume: $chapter->getVolume(),
totalPages: 0,
isVisible: $chapter->isVisible(),
createdAt: new \DateTimeImmutable(),
pagesDirectory: $chapter->getPagesDirectory(),
);
}
public function getTotalPagesForChapter(ChapterId $chapterId): int
{
$chapter = $this->entityManager->getRepository(ChapterEntity::class)->findOneBy([
'id' => $chapterId->getValue()
]);
if (!$chapter) {
throw ChapterNotFoundException::forChapter($chapterId);
}
$pagesDirectory = $chapter->getPagesDirectory();
if ($pagesDirectory && is_dir($pagesDirectory)) {
return count($this->getImageFiles($pagesDirectory));
}
return 0;
}
public function getPreviousChapterId(ChapterId $chapterId): ?ChapterId
{
$currentChapter = $this->entityManager->getRepository(ChapterEntity::class)->findOneBy([
'id' => $chapterId->getValue()
]);
$qb = $this->entityManager->createQueryBuilder();
$qb->select('c')
->from(ChapterEntity::class, 'c')
->where('c.manga = :manga')
->andWhere('c.number < :number')
->andWhere('c.visible = true')
->andWhere('c.pagesDirectory IS NOT NULL OR c.cbzPath IS NOT NULL')
->orderBy('c.number', 'DESC')
->setMaxResults(1)
->setParameters([
'manga' => $currentChapter->getManga(),
'number' => $currentChapter->getNumber()
]);
$previousChapter = $qb->getQuery()->getOneOrNullResult();
return $previousChapter ? new ChapterId((string) $previousChapter->getId()) : null;
}
public function getNextChapterId(ChapterId $chapterId): ?ChapterId
{
$currentChapter = $this->entityManager->getRepository(ChapterEntity::class)->findOneBy([
'id' => $chapterId->getValue()
]);
$qb = $this->entityManager->createQueryBuilder();
$qb->select('c')
->from(ChapterEntity::class, 'c')
->where('c.manga = :manga')
->andWhere('c.number > :number')
->andWhere('c.visible = true')
->andWhere('c.pagesDirectory IS NOT NULL OR c.cbzPath IS NOT NULL')
->orderBy('c.number', 'ASC')
->setMaxResults(1)
->setParameters([
'manga' => $currentChapter->getManga(),
'number' => $currentChapter->getNumber()
]);
$nextChapter = $qb->getQuery()->getOneOrNullResult();
return $nextChapter ? new ChapterId((string) $nextChapter->getId()) : null;
}
private function getImageFiles(string $pagesDirectory): array
{
$files = glob($pagesDirectory . '/*.{jpg,jpeg,png,webp,gif}', GLOB_BRACE) ?: [];
sort($files);
return $files;
}
private function getPagesFromDirectory(ChapterId $chapterId, string $pagesDirectory, int $page, int $itemsPerPage): array
{
$files = $this->getImageFiles($pagesDirectory);
$start = max(0, ($page - 1) * $itemsPerPage);
$end = min($start + $itemsPerPage, count($files));
$pages = [];
for ($i = $start; $i < $end; $i++) {
$imageSize = @getimagesize($files[$i]);
if ($imageSize === false) {
continue;
}
$pages[] = new Page(
basename($files[$i]),
new PageNumber($i + 1),
sprintf('/images/pages/%s/%s', basename($pagesDirectory), basename($files[$i])),
$imageSize[0],
$imageSize[1]
);
}
return $pages;
}
}