diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f08eff..9776be2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,43 +4,43 @@ All notable changes to `php-ebook` will be documented in this file. ## 0.3.32 - 2023-05-08 -- MetaTitle `slugSortWithSerie` fix +- MetaTitle `slugSortWithSerie` fix ## 0.3.31 - 2023-05-05 -- improve documentation +- improve documentation ## 0.3.30 - 2023-05-05 -- make `slugSortWithSerie` always generated (if title) +- make `slugSortWithSerie` always generated (if title) ## 0.3.20 - 2023-05-05 -- update `titleMeta` to `metaTitle` +- update `titleMeta` to `metaTitle` ## 0.3.10 - 2023-05-05 -- add `titleMeta()` to `BookEntity` with extra infos with title slug and series slug -- move `ComicMeta` to `Kiwilan\Ebook\Entity\ComicMeta` +- add `titleMeta()` to `BookEntity` with extra infos with title slug and series slug +- move `ComicMeta` to `Kiwilan\Ebook\Entity\ComicMeta` ## 0.3.0 - 2023-05-05 -- add `filename` to `Ebook` -- remove `path` from `BookEntity` (it's in `Ebook`) -- `Book` `manga` is default `UNKNOWN` -- add methods `toArray`, `toJson` and `__toString` to `Ebook`, `BookEntity`, `EpubOpf`, `CbaFormat` +- add `filename` to `Ebook` +- remove `path` from `BookEntity` (it's in `Ebook`) +- `Book` `manga` is default `UNKNOWN` +- add methods `toArray`, `toJson` and `__toString` to `Ebook`, `BookEntity`, `OpfMetadata`, `CbaMetadata` ## 0.2.10 - 2023-05-05 -- add `words` property +- add `words` property ## 0.2.0 - 2023-05-05 -- Move `BookEntity` `cover` to `Ebook` +- Move `BookEntity` `cover` to `Ebook` ## 0.1.01 - 2023-05-05 -- Update `kiwilan/php-archive` +- Update `kiwilan/php-archive` ## 0.1.0 - 2023-04-01 diff --git a/README.md b/README.md index 35bf445..d6cf4f8 100644 --- a/README.md +++ b/README.md @@ -70,12 +70,12 @@ composer require kiwilan/php-ebook ## Usage -With eBook files (`.epub`, `.cbz`, `.cbr`, `.cb7`, `.cbt`, `.pdf`) +With eBook files (`.epub`, `.cbz`, `.cba`, `.cbr`, `.cb7`, `.cbt`, `.pdf`) ```php $ebook = Ebook::read('path/to/archive.epub'); -$metadata = $ebook->metadata(); // EpubOpf|CbaFormat|null => metadata OPF for EPUB, metadata CBA for CBA +$metadata = $ebook->metadata(); // OpfMetadata|CbaMetadata|null => metadata OPF for EPUB, metadata CBA for CBA $format = $book->format(); // epub, pdf, cba $book = $ebook->book(); // ?BookEntity $cover = $ebook->cover(bool $convertBase64 = true); // string => cover as string ($toString convert base64) @@ -85,13 +85,31 @@ $extension = $ebook->extension(); // string $hasMetadata = $ebook->hasMetadata(); // bool ``` +### Metadata + +```php +$metadata = $ebook->metadata(); // OpfMetadata|CbaMetadata|null => metadata OPF for EPUB, metadata CBA for CBA + +// For OpfMetadata +$metadata->metadata(); // `metadata` entry from `.opf` file +$metadata->manifest(); // `manifest` entry from `.opf` file +$metadata->spine(); // `spine` entry from `.opf` file +$metadata->guide(); // `guide` entry from `.opf` file +$metadata->dcX(); // `dcX` entries from `.opf` file + +// For CbaMetadata, see docs https://anansi-project.github.io/docs/comicinfo/documentation +$metadata->writers(); // `writers` entry from `ComicInfo` format +$metadata->pencillers(); // `pencillers` entry from `ComicInfo` format +// more from `ComicInfo` format +``` + ### Book ```php $book = $ebook->book(); // BookEntity $book->title(); // string -$book->metaTitle(); // ?MetaTitle, with `slug` and `sort` properties for `title` and `series` +$book->metaTitle(); // ?MetaTitle, with slug and sort properties for `title` and `series` $book->authors(); // BookCreator[] (name: string, role: string) $book->authorFirst(); // ?BookCreator => First BookCreator (name: string, role: string) $book->description(); // ?string @@ -110,10 +128,10 @@ $book->words(); // ?int => `words` count in EPUB $book->editors(); // string[] => `editors` in CBA $book->review(); // ?string => `review` in CBA $book->web(); // ?string => `web` in CBA -$book->manga(); // ?MangaEnum => `manga` in CBA | Addtional data about mangas +$book->manga(); // ?MangaEnum => `manga` in CBA | Additional data about mangas $book->isBlackAndWhite(); // bool => `blackAndWhite` in CBA -$book->ageRating(); // ?AgeRatingEnum => `ageRating` in CBA | Addtional data about age rating -$book->comicMeta(); // ?ComicMeta => Addtional data for CBA +$book->ageRating(); // ?AgeRatingEnum => `ageRating` in CBA | Additional data about age rating +$book->comicMeta(); // ?ComicMeta => Additional data for CBA ``` ### MetaTitle @@ -123,15 +141,15 @@ Can be set if book's title is not null. ```php $metaTitle = $book->metaTitle(); // ?MetaTitle -$metaTitle->slug(); // string => slugify title -$metaTitle->slugSort(); // string => slugify title without determiners -$metaTitle->slugLang(); // string => slugify title with language +$metaTitle->slug(); // string => slugify title, like `the-clan-of-the-cave-bear` +$metaTitle->slugSort(); // string => slugify title without determiners, like `clan-of-the-cave-bear` +$metaTitle->slugLang(); // string => slugify title with language and type, like `the-clan-of-the-cave-bear-epub-en` -$metaTitle->serieSlug(); // ?string => slugify series title -$metaTitle->serieSort(); // ?string => slugify series title without determiners -$metaTitle->serieLang(); // ?string => slugify series title with language +$metaTitle->serieSlug(); // ?string => slugify series title, like `earths-children` +$metaTitle->serieSort(); // ?string => slugify series title without determiners, like `earths-children` +$metaTitle->serieLang(); // ?string => slugify series title with language and type, like `earths-children-epub-en` -$metaTitle->slugSortWithSerie(); // string => slugify title with series title and volume +$metaTitle->slugSortWithSerie(); // string => slugify title with series title and volume, like `earths-children-01_clan-of-the-cave-bear` ``` ## Testing diff --git a/composer.json b/composer.json index 6f2d9d3..b2c0197 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "kiwilan/php-ebook", "description": "PHP package to read metadata and extract covers from eBooks (.epub, .cbz, .cbr, .cb7, .cbt, .pdf).", - "version": "0.3.32", + "version": "0.3.40", "keywords": [ "php", "ebook", diff --git a/src/Cba/CbaCbam.php b/src/Cba/CbaCbam.php index 7a900ba..76df976 100644 --- a/src/Cba/CbaCbam.php +++ b/src/Cba/CbaCbam.php @@ -12,7 +12,7 @@ /** * @docs https://anansi-project.github.io/docs/comicinfo/schemas/v2.0 */ -class CbaCbam extends CbaFormat +class CbaCbam extends CbaMetadata { protected string $metadataFilename = 'ComicInfo.xml'; @@ -180,7 +180,7 @@ private function parse(): void $this->seriesGroup = $this->extract('SeriesGroup'); $ageRating = $this->extract('AgeRating'); - $this->ageRating = $ageRating ? AgeRatingEnum::tryFrom($ageRating) : null; + $this->ageRating = $ageRating ? AgeRatingEnum::tryFrom($ageRating) : AgeRatingEnum::UNKNOWN; $communityRating = $this->extract('CommunityRating'); $this->communityRating = $communityRating ? (float) $communityRating : null; diff --git a/src/Cba/CbaFormat.php b/src/Cba/CbaMetadata.php similarity index 99% rename from src/Cba/CbaFormat.php rename to src/Cba/CbaMetadata.php index 6614515..cf3de52 100644 --- a/src/Cba/CbaFormat.php +++ b/src/Cba/CbaMetadata.php @@ -7,7 +7,7 @@ use Kiwilan\Ebook\Enums\AgeRatingEnum; use Kiwilan\Ebook\Enums\MangaEnum; -abstract class CbaFormat +abstract class CbaMetadata { /** @var string[] */ protected array $writers = []; diff --git a/src/Ebook.php b/src/Ebook.php index 9caff68..2ed7e63 100755 --- a/src/Ebook.php +++ b/src/Ebook.php @@ -7,13 +7,13 @@ use Kiwilan\Ebook\Book\BookCreator; use Kiwilan\Ebook\Cba\Cba; use Kiwilan\Ebook\Cba\CbaCbam; -use Kiwilan\Ebook\Cba\CbaFormat; +use Kiwilan\Ebook\Cba\CbaMetadata; use Kiwilan\Ebook\Epub\EpubContainer; -use Kiwilan\Ebook\Epub\EpubOpf; +use Kiwilan\Ebook\Epub\OpfMetadata; class Ebook { - protected EpubOpf|CbaFormat|null $metadata = null; + protected OpfMetadata|CbaMetadata|null $metadata = null; protected ?BookEntity $book = null; @@ -70,7 +70,7 @@ private function epub(): self if (! $opf) { return $this; } - $opf = EpubOpf::make($opf); + $opf = OpfMetadata::make($opf); $this->metadata = $opf; $this->book = $opf->toBook(); @@ -111,7 +111,7 @@ private function cba(): self default => null, }; - /** @var ?CbaFormat */ + /** @var ?CbaMetadata */ $parser = match ($metadataType) { 'cbam' => CbaCbam::class, // 'cbml' => CbaCbml::class, @@ -243,7 +243,7 @@ public function format(): ?string /** * Metadata of the ebook. */ - public function metadata(): EpubOpf|CbaFormat|null + public function metadata(): OpfMetadata|CbaMetadata|null { return $this->metadata; } diff --git a/src/Entity/MetaTitle.php b/src/Entity/MetaTitle.php index 3db0ae8..40c96b8 100644 --- a/src/Entity/MetaTitle.php +++ b/src/Entity/MetaTitle.php @@ -76,7 +76,7 @@ private function setMetaTitle(Ebook $ebook): static } /** - * Get slug of book title, like `le-clan-de-lours-des-cavernes`. + * Get slug of book title, like `the-clan-of-the-cave-bear`. */ public function slug(): string { @@ -84,7 +84,7 @@ public function slug(): string } /** - * Get slug of book title without determiners, like `clan-de-lours-des-cavernes`. + * Get slug of book title without determiners, like `clan-of-the-cave-bear`. */ public function slugSort(): string { @@ -92,7 +92,7 @@ public function slugSort(): string } /** - * Get slug of book title with language, like `le-clan-de-lours-des-cavernes-epub-fr`. + * Get slug of book title with language and with type, like `the-clan-of-the-cave-bear-epub-en`. */ public function slugLang(): string { @@ -100,7 +100,7 @@ public function slugLang(): string } /** - * Get slug of serie title, like `les-enfants-de-la-terre`. + * Get slug of serie title, like `earths-children`. */ public function serieSlug(): ?string { @@ -108,7 +108,7 @@ public function serieSlug(): ?string } /** - * Get slug of serie title without determiners, like `enfants-de-la-terre`. + * Get slug of serie title without determiners, like `earths-children`. */ public function serieSlugSort(): ?string { @@ -116,7 +116,7 @@ public function serieSlugSort(): ?string } /** - * Get slug of serie title with language, like `les-enfants-de-la-terre-epub-fr`. + * Get slug of serie title with language and with type, like `earths-children-epub-en`. */ public function serieSlugLang(): ?string { @@ -124,8 +124,8 @@ public function serieSlugLang(): ?string } /** - * Get slug of book title with serie title, like `enfants-de-la-terre-01_clan-de-lours-des-cavernes`. - * If series is null, book's title will be used like `clan-de-lours-des-cavernes`. + * Get slug of book title with serie title, like `earths-children-01_clan-of-the-cave-bear`. + * If series is null, book's title will be used like `clan-of-the-cave-bear`. */ public function slugSortWithSerie(): string { @@ -182,7 +182,7 @@ private function generateSortSerie(string $title, ?string $serieTitle, ?int $vol } /** - * Generate `slug` with `title`, `BookTypeEnum` and `language_slug`. + * Generate `slug` with `title`, `type` and `language`. */ private function generateSlug(string $title, ?string $type, ?string $language): string { @@ -225,6 +225,9 @@ private function setSlug(?string $title, string $separator = '-', array $diction return null; } + $transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: Lower(); :: NFC;', Transliterator::FORWARD); + $title = $transliterator->transliterate($title); + // Convert all dashes/underscores into separator $flip = $separator === '-' ? '_' : '-'; diff --git a/src/Epub/EpubOpf.php b/src/Epub/OpfMetadata.php similarity index 97% rename from src/Epub/EpubOpf.php rename to src/Epub/OpfMetadata.php index 7627ac3..123b0a0 100644 --- a/src/Epub/EpubOpf.php +++ b/src/Epub/OpfMetadata.php @@ -11,7 +11,7 @@ use Kiwilan\Ebook\BookEntity; use Kiwilan\Ebook\XmlReader; -class EpubOpf +class OpfMetadata { protected array $metadata = []; @@ -230,6 +230,26 @@ private function findContent(): array return $files; } + public function metadata(): array + { + return $this->metadata; + } + + public function manifest(): array + { + return $this->manifest; + } + + public function spine(): array + { + return $this->spine; + } + + public function guide(): array + { + return $this->guide; + } + public function epubVersion(): ?int { return $this->epubVersion; diff --git a/tests/CbaTest.php b/tests/CbaTest.php index cf5a0d2..14ab88a 100644 --- a/tests/CbaTest.php +++ b/tests/CbaTest.php @@ -4,7 +4,7 @@ // CBML comic-book-markup language with ComicBook.xml use Kiwilan\Ebook\Cba\CbaCbam; -use Kiwilan\Ebook\Cba\CbaFormat; +use Kiwilan\Ebook\Cba\CbaMetadata; use Kiwilan\Ebook\Entity\ComicMeta; use Kiwilan\Ebook\Enums\AgeRatingEnum; use Kiwilan\Ebook\Enums\MangaEnum; @@ -96,7 +96,7 @@ expect($book->date()->format('Y-m-d'))->toBe($date->format('Y-m-d')); expect($book->pageCount())->toBe(24); expect($book->manga())->toBe(MangaEnum::NO); - expect($book->ageRating())->toBe(AgeRatingEnum::UNKNOWN); + expect($book->ageRating())->toBe(AgeRatingEnum::TEEN); expect($book->comicMeta()->imprint())->toBe('Vertigo'); expect($book->comicMeta()->characters())->toBeArray(); @@ -112,11 +112,11 @@ expect($book->comicMeta()->storyArcNumber())->toBeNull(); })->with([CBZ_CBAM]); -it('can parse CbaFormat', function (string $path) { +it('can parse CbaMetadata', function (string $path) { $ebook = Kiwilan\Ebook\Ebook::read($path); $metadata = $ebook->metadata(); - expect($metadata)->toBeInstanceOf(CbaFormat::class); + expect($metadata)->toBeInstanceOf(CbaMetadata::class); expect($metadata->title())->toBe('You Had One Job'); expect($metadata->series())->toBe('Fantastic Four'); expect($metadata->number())->toBe(22); @@ -143,7 +143,8 @@ expect($metadata->alternateSeries())->toBe('Empyre'); expect($metadata->seriesGroup())->toBe('Fantastic Four'); - expect($metadata->ageRating())->toBeNull(); + dump($metadata->ageRating()); + expect($metadata->ageRating())->toBe(AgeRatingEnum::TEEN); expect($metadata->manga())->toBe(MangaEnum::NO); expect($metadata->pageCount())->toBe(24); expect($metadata->imprint())->toBe('Vertigo'); diff --git a/tests/EpubOpfTest.php b/tests/EpubOpfTest.php index 23271be..0746e4e 100644 --- a/tests/EpubOpfTest.php +++ b/tests/EpubOpfTest.php @@ -1,7 +1,8 @@ EpubContainer::make(file_get_contents(EPUB_CONTAINER_EPUB2_EMPTY)))->toThrow(Exception::class); }); +it('can failed with wrong XML', function () { + expect(fn () => XmlReader::toArray('
'))->toThrow(Exception::class); +}); + it('can parse epub opf', function (string $path) { - $opf = EpubOpf::make(file_get_contents($path)); + $opf = OpfMetadata::make(file_get_contents($path)); - expect($opf)->tobeInstanceOf(EpubOpf::class); + expect($opf)->tobeInstanceOf(OpfMetadata::class); expect($path)->toBeReadableFile(); expect($opf->dcTitle())->toBeString(); expect($opf->dcCreators())->toBeArray(); @@ -39,8 +44,12 @@ })->with([EPUB_OPF_EPUB2, EPUB_OPF_EPUB3]); it('can parse epub opf alt', function () { - $opf = EpubOpf::make(file_get_contents(EPUB_OPF_EPUB3_ALT)); + $opf = OpfMetadata::make(file_get_contents(EPUB_OPF_EPUB3_ALT)); + expect($opf->metadata())->toBeArray(); + expect($opf->manifest())->toBeArray(); + expect($opf->spine())->toBeArray(); + expect($opf->guide())->toBeArray(); expect($opf->dcTitle())->toBeString(); expect($opf->dcCreators())->toBeArray(); expect($opf->dcDescription())->toBeString(); diff --git a/tests/MetaTitleTest.php b/tests/MetaTitleTest.php new file mode 100644 index 0000000..f4b8cd5 --- /dev/null +++ b/tests/MetaTitleTest.php @@ -0,0 +1,21 @@ +book()->setTitle('La pâle lumière des ténèbres'); + $ebook->book()->setVolume(1); + $ebook->book()->setSeries('A comme Association'); + $meta = MetaTitle::make($ebook); + + expect($meta->slug())->toBe('la-pale-lumiere-des-tenebres'); + expect($meta->slugSort())->toBe('pale-lumiere-des-tenebres'); + expect($meta->slugLang())->toBe('la-pale-lumiere-des-tenebres-epub-fr'); + expect($meta->serieSlug())->toBe('a-comme-association'); + expect($meta->serieSlugSort())->toBe('a-comme-association'); + expect($meta->serieSlugLang())->toBe('a-comme-association-epub-fr'); + expect($meta->slugSortWithSerie())->toBe('a-comme-association-01_pale-lumiere-des-tenebres'); +}); diff --git a/tests/media/cba-cbam.cbz b/tests/media/cba-cbam.cbz index 3ae20ed..c25135f 100644 Binary files a/tests/media/cba-cbam.cbz and b/tests/media/cba-cbam.cbz differ diff --git a/tests/media/cba-cbam.xml b/tests/media/cba-cbam.xml index 4d8f37e..93e6e1b 100644 --- a/tests/media/cba-cbam.xml +++ b/tests/media/cba-cbam.xml @@ -34,7 +34,7 @@