Version 5.1 - All the GraphQL #32
@ -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 ?>
|
||||
|
@ -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
|
||||
*
|
||||
|
29
src/AnimeClient/API/Kitsu/Enum/MangaPublishingStatus.php
Normal file
29
src/AnimeClient/API/Kitsu/Enum/MangaPublishingStatus.php
Normal 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
|
112
src/AnimeClient/API/Kitsu/GraphQL/Queries/MangaDetails.graphql
Normal file
112
src/AnimeClient/API/Kitsu/GraphQL/Queries/MangaDetails.graphql
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
*
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user