From 83de99595176c1d6ae3711b206f7cb5d1c4a685c Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Wed, 29 Jul 2020 17:51:58 -0400 Subject: [PATCH] Get Manga details from GraphQL, See #27 --- app/views/manga/details.php | 20 ++- src/AnimeClient/API/Kitsu.php | 23 +++ .../API/Kitsu/Enum/MangaPublishingStatus.php | 29 ++++ .../GraphQL/Queries/MangaDetails.graphql | 112 +++++++++++++++ .../GraphQL/Queries/MangaDetailsById.graphql | 112 +++++++++++++++ src/AnimeClient/API/Kitsu/KitsuMangaTrait.php | 32 +++-- .../Kitsu/Transformer/MangaTransformer.php | 136 ++++++++---------- src/AnimeClient/Types/MangaPage.php | 52 ++++--- 8 files changed, 403 insertions(+), 113 deletions(-) create mode 100644 src/AnimeClient/API/Kitsu/Enum/MangaPublishingStatus.php create mode 100644 src/AnimeClient/API/Kitsu/GraphQL/Queries/MangaDetails.graphql create mode 100644 src/AnimeClient/API/Kitsu/GraphQL/Queries/MangaDetailsById.graphql diff --git a/app/views/manga/details.php b/app/views/manga/details.php index 877a929f..cff3ef19 100644 --- a/app/views/manga/details.php +++ b/app/views/manga/details.php @@ -7,6 +7,10 @@ + + + + @@ -17,7 +21,15 @@ - + */ ?> + + + + + + +
Publishing Status
Manga Type
Chapter Count
Age Rating +
Genres @@ -82,16 +94,16 @@ type="radio" name="staff-roles" id="staff-role" />
- $person): ?> + diff --git a/src/AnimeClient/API/Kitsu.php b/src/AnimeClient/API/Kitsu.php index 1ef7ae97..fb1ece76 100644 --- a/src/AnimeClient/API/Kitsu.php +++ b/src/AnimeClient/API/Kitsu.php @@ -17,6 +17,7 @@ namespace Aviat\AnimeClient\API; use Aviat\AnimeClient\API\Kitsu\Enum\AnimeAiringStatus; +use Aviat\AnimeClient\API\Kitsu\Enum\MangaPublishingStatus; use DateTimeImmutable; /** @@ -65,6 +66,28 @@ final class Kitsu { return AnimeAiringStatus::NOT_YET_AIRED; } + public static function getPublishingStatus(string $kitsuStatus, string $startDate = NULL, string $endDate = NULL): string + { + $startPubDate = new DateTimeImmutable($startDate ?? 'tomorrow'); + $endPubDate = new DateTimeImmutable($endDate ?? 'next year'); + $now = new DateTimeImmutable(); + + $isDone = $now > $endPubDate; + $isCurrent = ($now > $startPubDate) && ! $isDone; + + if ($kitsuStatus === 'CURRENT' || $isCurrent) + { + return MangaPublishingStatus::CURRENT; + } + + if ($kitsuStatus === 'FINISHED' || $isDone) + { + return MangaPublishingStatus::FINISHED; + } + + return MangaPublishingStatus::NOT_YET_PUBLISHED; + } + /** * Reorganize streaming links * diff --git a/src/AnimeClient/API/Kitsu/Enum/MangaPublishingStatus.php b/src/AnimeClient/API/Kitsu/Enum/MangaPublishingStatus.php new file mode 100644 index 00000000..dc0949aa --- /dev/null +++ b/src/AnimeClient/API/Kitsu/Enum/MangaPublishingStatus.php @@ -0,0 +1,29 @@ + + * @copyright 2015 - 2020 Timothy J. Warren + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @version 5 + * @link https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient + */ + +namespace Aviat\AnimeClient\API\Kitsu\Enum; + +use Aviat\Ion\Enum as BaseEnum; + +/** + * Status of when anime is being/was/will be aired + */ +final class MangaPublishingStatus extends BaseEnum { + public const NOT_YET_PUBLISHED = 'Not Yet Published'; + public const FINISHED = 'Completed'; + public const CURRENT = 'Current'; +} +// End of MangaPublishingStatus diff --git a/src/AnimeClient/API/Kitsu/GraphQL/Queries/MangaDetails.graphql b/src/AnimeClient/API/Kitsu/GraphQL/Queries/MangaDetails.graphql new file mode 100644 index 00000000..14a7a4ab --- /dev/null +++ b/src/AnimeClient/API/Kitsu/GraphQL/Queries/MangaDetails.graphql @@ -0,0 +1,112 @@ +query ($slug: String!) { + findMangaBySlug(slug: $slug) { + id + ageRating + ageRatingGuide + posterImage { + original { + height + name + url + width + } + views { + height + name + url + width + } + } + categories { + nodes { + title + } + } + characters { + nodes { + character { + id + names { + canonical + alternatives + } + image { + original { + height + name + url + width + } + } + slug + } + role + } + pageInfo { + endCursor + hasNextPage + hasPreviousPage + startCursor + } + } + startDate + endDate + posterImage { + original { + height + name + url + width + } + views { + height + name + url + width + } + } + sfw + slug + staff { + nodes { + person { + id + birthday + image { + original { + height + name + url + width + } + views { + height + name + url + width + } + } + names { + alternatives + canonical + localized + } + } + role + } + pageInfo { + endCursor + hasNextPage + hasPreviousPage + startCursor + } + } + status + synopsis + titles { + canonical + canonicalLocale + localized + } + } +} diff --git a/src/AnimeClient/API/Kitsu/GraphQL/Queries/MangaDetailsById.graphql b/src/AnimeClient/API/Kitsu/GraphQL/Queries/MangaDetailsById.graphql new file mode 100644 index 00000000..68bfb31b --- /dev/null +++ b/src/AnimeClient/API/Kitsu/GraphQL/Queries/MangaDetailsById.graphql @@ -0,0 +1,112 @@ +query ($id: ID!) { + findMangaById(id: $id) { + id + ageRating + ageRatingGuide + posterImage { + original { + height + name + url + width + } + views { + height + name + url + width + } + } + categories { + nodes { + title + } + } + characters { + nodes { + character { + id + names { + canonical + alternatives + } + image { + original { + height + name + url + width + } + } + slug + } + role + } + pageInfo { + endCursor + hasNextPage + hasPreviousPage + startCursor + } + } + startDate + endDate + posterImage { + original { + height + name + url + width + } + views { + height + name + url + width + } + } + sfw + slug + staff { + nodes { + person { + id + birthday + image { + original { + height + name + url + width + } + views { + height + name + url + width + } + } + names { + alternatives + canonical + localized + } + } + role + } + pageInfo { + endCursor + hasNextPage + hasPreviousPage + startCursor + } + } + status + synopsis + titles { + canonical + canonicalLocale + localized + } + } +} diff --git a/src/AnimeClient/API/Kitsu/KitsuMangaTrait.php b/src/AnimeClient/API/Kitsu/KitsuMangaTrait.php index 9c844bd0..cf01f9e7 100644 --- a/src/AnimeClient/API/Kitsu/KitsuMangaTrait.php +++ b/src/AnimeClient/API/Kitsu/KitsuMangaTrait.php @@ -56,7 +56,10 @@ trait KitsuMangaTrait { */ public function getManga(string $slug): MangaPage { - $baseData = $this->getRawMediaData('manga', $slug); + $baseData = $this->requestBuilder->runQuery('MangaDetails', [ + 'slug' => $slug + ]); + // $baseData = $this->getRawMediaData('manga', $slug); if (empty($baseData)) { @@ -66,6 +69,21 @@ trait KitsuMangaTrait { return $this->mangaTransformer->transform($baseData); } + /** + * Get information about a particular manga + * + * @param string $mangaId + * @return MangaPage + */ + public function getMangaById(string $mangaId): MangaPage + { + $baseData = $this->requestBuilder->runQuery('MangaDetailsById', [ + 'id' => $mangaId, + ]); + // $baseData = $this->getRawMediaDataById('manga', $mangaId); + return $this->mangaTransformer->transform($baseData); + } + /** * Retrieve the data for the manga read history page * @@ -92,18 +110,6 @@ trait KitsuMangaTrait { return $list; } - /** - * Get information about a particular manga - * - * @param string $mangaId - * @return MangaPage - */ - public function getMangaById(string $mangaId): MangaPage - { - $baseData = $this->getRawMediaDataById('manga', $mangaId); - return $this->mangaTransformer->transform($baseData); - } - /** * Get the manga list for the configured user * diff --git a/src/AnimeClient/API/Kitsu/Transformer/MangaTransformer.php b/src/AnimeClient/API/Kitsu/Transformer/MangaTransformer.php index 2d428537..9882abb8 100644 --- a/src/AnimeClient/API/Kitsu/Transformer/MangaTransformer.php +++ b/src/AnimeClient/API/Kitsu/Transformer/MangaTransformer.php @@ -16,7 +16,7 @@ namespace Aviat\AnimeClient\API\Kitsu\Transformer; -use Aviat\AnimeClient\API\JsonAPI; +use Aviat\AnimeClient\API\Kitsu; use Aviat\AnimeClient\Types\MangaPage; use Aviat\Ion\Transformer\AbstractTransformer; @@ -34,106 +34,90 @@ final class MangaTransformer extends AbstractTransformer { */ public function transform($item): MangaPage { - $genres = []; + // TODO: missing GraphQL data: + // * chapter count + // * volume count + // * manga type - $item['included'] = JsonAPI::organizeIncluded($item['included']); - - if (array_key_exists('categories', $item['included'])) - { - foreach ($item['included']['categories'] as $cat) - { - $genres[] = $cat['attributes']['title']; - } - sort($genres); - } - - $title = $item['canonicalTitle']; - $rawTitles = array_values($item['titles']); - $titles = array_unique(array_diff($rawTitles, [$title])); + $base = array_key_exists('findMangaBySlug', $item['data']) + ? $item['data']['findMangaBySlug'] + : $item['data']['findMangaById']; $characters = []; $staff = []; + $genres = array_map(fn ($genre) => $genre['title']['en'], $base['categories']['nodes']); + sort($genres); - if (array_key_exists('mediaCharacters', $item['included'])) + $title = $base['titles']['canonical']; + $titles = Kitsu::filterLocalizedTitles($base['titles']); + + if (count($base['characters']['nodes']) > 0) { - $mediaCharacters = $item['included']['mediaCharacters']; + $characters['main'] = []; + $characters['supporting'] = []; - foreach ($mediaCharacters as $rel) + foreach ($base['characters']['nodes'] as $rawCharacter) { - // dd($rel); - // $charId = $rel['relationships']['character']['data']['id']; - $role = $rel['attributes']['role']; + $type = $rawCharacter['role'] === 'MAIN' ? 'main' : 'supporting'; + $details = $rawCharacter['character']; + $characters[$type][$details['id']] = [ + 'image' => $details['image'], + 'name' => $details['names']['canonical'], + 'slug' => $details['slug'], + ]; + } - foreach ($rel['relationships']['character']['characters'] as $charId => $char) - { - if (array_key_exists($charId, $item['included']['characters'])) - { - $characters[$role][$charId] = $char['attributes']; - } - } + uasort($characters['main'], fn($a, $b) => $a['name'] <=> $b['name']); + uasort($characters['supporting'], fn($a, $b) => $a['name'] <=> $b['name']); + + if (empty($characters['supporting'])) + { + unset($characters['supporting']); } } - if (array_key_exists('mediaStaff', $item['included'])) + if (count($base['staff']['nodes']) > 0) { - foreach ($item['included']['mediaStaff'] as $id => $staffing) + foreach ($base['staff']['nodes'] as $staffing) { - $role = $staffing['attributes']['role']; + $person = $staffing['person']; + $role = $staffing['role']; + $name = $person['names']['localized'][$person['names']['canonical']]; - foreach ($staffing['relationships']['person']['people'] as $personId => $personDetails) + if ( ! array_key_exists($role, $staff)) { - if ( ! array_key_exists($role, $staff)) - { - $staff[$role] = []; - } - - $staff[$role][$personId] = [ - 'id' => $personId, - 'name' => $personDetails['attributes']['name'] ?? '??', - 'image' => $personDetails['attributes']['image'], - ]; + $staff[$role] = []; } + + $staff[$role][$person['id']] = [ + 'id' => $person['id'], + 'name' => $name, + 'image' => [ + 'original' => $person['image']['original']['url'], + ], + ]; + + usort($staff[$role], fn ($a, $b) => $a['name'] <=> $b['name']); } + + ksort($staff); } - if ( ! empty($characters['main'])) - { - uasort($characters['main'], fn ($a, $b) => $a['name'] <=> $b['name']); - } - - if ( ! empty($characters['supporting'])) - { - uasort($characters['supporting'], fn ($a, $b) => $a['name'] <=> $b['name']); - } - - ksort($characters); - ksort($staff); - - return MangaPage::from([ + $data = [ + 'age_rating' => $base['ageRating'], + 'age_rating_guide' => $base['ageRatingGuide'], 'characters' => $characters, - 'chapter_count' => $this->count($item['chapterCount']), - 'cover_image' => $item['posterImage']['small'], + 'cover_image' => $base['posterImage']['views'][1]['url'], 'genres' => $genres, - 'id' => $item['id'], - 'included' => $item['included'], - 'manga_type' => $item['mangaType'], + 'id' => $base['id'], 'staff' => $staff, - 'synopsis' => $item['synopsis'], + 'status' => Kitsu::getPublishingStatus($base['status'], $base['startDate'], $base['endDate']), + 'synopsis' => $base['synopsis']['en'], 'title' => $title, 'titles' => $titles, - 'url' => "https://kitsu.io/manga/{$item['slug']}", - 'volume_count' => $this->count($item['volumeCount']), - ]); - } + 'url' => "https://kitsu.io/manga/{$base['slug']}", + ]; - /** - * @param int|null $value - * @return string - */ - private function count(int $value = NULL): string - { - return ((int)$value === 0) - ? '-' - : (string)$value; + return MangaPage::from($data); } } \ No newline at end of file diff --git a/src/AnimeClient/Types/MangaPage.php b/src/AnimeClient/Types/MangaPage.php index 3db6679c..18eb2d2f 100644 --- a/src/AnimeClient/Types/MangaPage.php +++ b/src/AnimeClient/Types/MangaPage.php @@ -16,10 +16,22 @@ namespace Aviat\AnimeClient\Types; +use Aviat\AnimeClient\API\Kitsu\Enum\MangaPublishingStatus; + /** * Type representing an Anime object for display */ final class MangaPage extends AbstractType { + /** + * @var string + */ + public ?string $age_rating; + + /** + * @var string + */ + public ?string $age_rating_guide; + /** * @var array */ @@ -45,43 +57,43 @@ final class MangaPage extends AbstractType { */ public $id; - /** - * @var array - */ - public $included; - /** * @var string */ public $manga_type; /** - * @var array + * @var MangaPublishingStatus */ - public $staff; - - /** - * @var string - */ - public $synopsis; - - /** - * @var string - */ - public $title; + public string $status = MangaPublishingStatus::FINISHED; /** * @var array */ - public $titles; + public array $staff; /** * @var string */ - public $url; + public string $synopsis; + + /** + * @var string + */ + public string $title; + + /** + * @var array + */ + public array $titles; + + /** + * @var string + */ + public string $url; /** * @var int */ - public $volume_count; + public ?int $volume_count; }