Get Manga details from GraphQL, See #27
timw4mail/HummingBirdAnimeClient/pipeline/head Something is wrong with the build of this commit Details

This commit is contained in:
Timothy Warren 2020-07-29 17:51:58 -04:00
parent 766e3cd71c
commit 83de995951
8 changed files with 403 additions and 113 deletions

View File

@ -7,6 +7,10 @@
<table class="media-details">
<tr>
<td class="align-right">Publishing Status</td>
<td><?= $data['status'] ?></td>
</tr>
<?php /* <tr>
<td>Manga Type</td>
<td><?= ucfirst($data['manga_type']) ?></td>
</tr>
@ -17,7 +21,15 @@
<tr>
<td>Chapter Count</td>
<td><?= $data['chapter_count'] ?></td>
</tr>
</tr> */ ?>
<?php if ( ! empty($data['age_rating'])): ?>
<tr>
<td>Age Rating</td>
<td><abbr title="<?= $data['age_rating_guide'] ?>"><?= $data['age_rating'] ?></abbr>
</td>
</tr>
<?php endif ?>
<tr>
<td>Genres</td>
<td>
@ -82,16 +94,16 @@
type="radio" name="staff-roles" id="staff-role<?= $i ?>" <?= $i === 0 ? 'checked' : '' ?> />
<label for="staff-role<?= $i ?>"><?= $role ?></label>
<section class='content media-wrap flex flex-wrap flex-justify-start'>
<?php foreach ($people as $pid => $person): ?>
<?php foreach ($people as $person): ?>
<article class='character person'>
<?php $link = $url->generate('person', ['id' => $pid]) ?>
<?php $link = $url->generate('person', ['id' => $person['id']]) ?>
<div class="name">
<a href="<?= $link ?>">
<?= $person['name'] ?>
</a>
</div>
<a href="<?= $link ?>">
<?= $helper->picture("images/people/{$pid}.webp") ?>
<?= $helper->picture("images/people/{$person['id']}.webp") ?>
</a>
</article>
<?php endforeach ?>

View File

@ -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
*

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
/**
* Hummingbird Anime List Client
*
* An API client for Kitsu to manage anime and manga watch lists
*
* PHP version 7.4
*
* @package HummingbirdAnimeClient
* @author Timothy J. Warren <tim@timshomepage.net>
* @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

View File

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

View File

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

View File

@ -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
*

View File

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

View File

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