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; } }