forked from timw4mail/HummingBirdAnimeClient
Get Person detail pages via GraphQL, resolves #27
This commit is contained in:
parent
1a3f1e9654
commit
a14ac3a122
|
@ -186,9 +186,8 @@ $routes = [
|
|||
]
|
||||
],
|
||||
'person' => [
|
||||
'path' => '/people/{id}{/slug}',
|
||||
'path' => '/people/{slug}',
|
||||
'tokens' => [
|
||||
'id' => SLUG_PATTERN,
|
||||
'slug' => SLUG_PATTERN,
|
||||
]
|
||||
],
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<section class="<?= $className ?>">
|
||||
<?php foreach ($data as $tabName => $tabData): ?>
|
||||
<?= $callback($tabData, $tabName) ?>
|
||||
<?php endforeach ?>
|
||||
</section>
|
|
@ -186,7 +186,7 @@ use function Aviat\AnimeClient\getLocalImg;
|
|||
}
|
||||
$rendered[] = $component->character(
|
||||
$person['name'],
|
||||
$url->generate('person', ['id' => $person['id'], 'slug' => $person['slug']]),
|
||||
$url->generate('person', ['slug' => $person['slug']]),
|
||||
$helper->picture(getLocalImg($person['image']['original'] ?? NULL)),
|
||||
'character small-person',
|
||||
);
|
||||
|
|
|
@ -120,10 +120,7 @@ use Aviat\AnimeClient\Kitsu;
|
|||
foreach ($casting as $id => $c):
|
||||
$person = $component->character(
|
||||
$c['person']['name'],
|
||||
$url->generate('person', [
|
||||
'id' => $c['person']['id'],
|
||||
'slug' => $c['person']['slug']
|
||||
]),
|
||||
$url->generate('person', ['slug' => $c['person']['slug']]),
|
||||
$helper->picture(getLocalImg($c['person']['image']))
|
||||
);
|
||||
$medias = array_map(fn ($series) => $component->media(
|
||||
|
|
|
@ -95,7 +95,7 @@
|
|||
fn($people) => implode('', array_map(
|
||||
fn ($person) => $component->character(
|
||||
$person['name'],
|
||||
$url->generate('person', ['id' => $person['id'], 'slug' => $person['slug']]),
|
||||
$url->generate('person', ['slug' => $person['slug']]),
|
||||
$helper->picture("images/people/{$person['id']}.webp")
|
||||
),
|
||||
$people
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<?php
|
||||
use function Aviat\AnimeClient\getLocalImg;
|
||||
use Aviat\AnimeClient\Kitsu;
|
||||
?>
|
||||
<main class="details fixed">
|
||||
<section class="flex flex-no-wrap">
|
||||
|
@ -9,6 +8,14 @@ use Aviat\AnimeClient\Kitsu;
|
|||
</div>
|
||||
<div>
|
||||
<h2 class="toph"><?= $data['name'] ?></h2>
|
||||
<?php foreach ($data['names'] as $name): ?>
|
||||
<h3><?= $name ?></h3>
|
||||
<?php endforeach ?>
|
||||
<br />
|
||||
<hr />
|
||||
<div class="description">
|
||||
<p><?= str_replace("\n", '</p><p>', $data['description']) ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
@ -24,7 +31,6 @@ use Aviat\AnimeClient\Kitsu;
|
|||
type="radio" name="staff-roles" id="staff-role<?= $i ?>" <?= $i === 0 ? 'checked' : '' ?> />
|
||||
<label for="staff-role<?= $i ?>"><?= $role ?></label>
|
||||
<?php foreach ($entries as $type => $casting): ?>
|
||||
<?php if ($type === 'characters') continue; ?>
|
||||
<?php if (isset($entries['manga'], $entries['anime'])): ?>
|
||||
<h4><?= ucfirst($type) ?></h4>
|
||||
<?php endif ?>
|
||||
|
@ -32,7 +38,7 @@ use Aviat\AnimeClient\Kitsu;
|
|||
<?php foreach ($casting as $sid => $series): ?>
|
||||
<?php $mediaType = in_array($type, ['anime', 'manga'], TRUE) ? $type : 'anime'; ?>
|
||||
<?= $component->media(
|
||||
Kitsu::filterTitles($series),
|
||||
$series['titles'],
|
||||
$url->generate("{$mediaType}.details", ['id' => $series['slug']]),
|
||||
$helper->picture("images/{$type}/{$sid}.webp")
|
||||
) ?>
|
||||
|
@ -46,7 +52,7 @@ use Aviat\AnimeClient\Kitsu;
|
|||
</section>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if ( ! (empty($data['characters']['main']) || empty($data['characters']['supporting']))): ?>
|
||||
<?php if ( ! empty($data['characters'])): ?>
|
||||
<section>
|
||||
<h3>Voice Acting Roles</h3>
|
||||
<?= $component->tabs('voice-acting-roles', $data['characters'], static function ($characterList) use ($component, $helper, $url) {
|
||||
|
@ -61,7 +67,7 @@ use Aviat\AnimeClient\Kitsu;
|
|||
foreach ($item['media'] as $sid => $series)
|
||||
{
|
||||
$medias[] = $component->media(
|
||||
Kitsu::filterTitles($series),
|
||||
$series['titles'],
|
||||
$url->generate('anime.details', ['id' => $series['slug']]),
|
||||
$helper->picture("images/anime/{$sid}.webp")
|
||||
);
|
||||
|
|
|
@ -163,7 +163,7 @@ CSS Tabs
|
|||
/* text-align: center; */
|
||||
}
|
||||
|
||||
.tabs .content {
|
||||
.tabs .content, .single-tab {
|
||||
display: none;
|
||||
max-height: 950px;
|
||||
border: 1px solid #e5e5e5;
|
||||
|
@ -175,7 +175,14 @@ CSS Tabs
|
|||
overflow: auto;
|
||||
}
|
||||
|
||||
.tabs .content.full-height {
|
||||
.single-tab {
|
||||
display: block;
|
||||
border: 1px solid #e5e5e5;
|
||||
box-shadow: 0 48px 80px -32px rgba(0, 0, 0, 0.3);
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
|
||||
.tabs .content.full-height, .single-tab.full-height {
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
|
|
|
@ -147,7 +147,8 @@ button:active {
|
|||
.tabs > [type="radio"]:checked + label,
|
||||
.tabs > [type="radio"]:checked + label + .content,
|
||||
.vertical-tabs [type="radio"]:checked + label,
|
||||
.vertical-tabs [type="radio"]:checked ~ .content {
|
||||
.vertical-tabs [type="radio"]:checked ~ .content,
|
||||
.single-tab {
|
||||
/* border-color: #333; */
|
||||
border: 0;
|
||||
background: #666;
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -199,27 +199,14 @@ final class Model {
|
|||
/**
|
||||
* Get information about a person
|
||||
*
|
||||
* @param string $id
|
||||
* @param string $slug
|
||||
* @return array
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function getPerson(string $id): array
|
||||
public function getPerson(string $slug): array
|
||||
{
|
||||
return $this->getCached("kitsu-person-{$id}", fn () => $this->requestBuilder->getRequest("people/{$id}", [
|
||||
'query' => [
|
||||
'filter' => [
|
||||
'id' => $id,
|
||||
],
|
||||
'fields' => [
|
||||
'characters' => 'canonicalName,slug,image',
|
||||
'characterVoices' => 'mediaCharacter',
|
||||
'anime' => 'canonicalTitle,abbreviatedTitles,titles,slug,posterImage',
|
||||
'manga' => 'canonicalTitle,abbreviatedTitles,titles,slug,posterImage',
|
||||
'mediaCharacters' => 'role,media,character',
|
||||
'mediaStaff' => 'role,media,person',
|
||||
],
|
||||
'include' => 'voices.mediaCharacter.media,voices.mediaCharacter.character,staff.media',
|
||||
],
|
||||
return $this->getCached("kitsu-person-{$slug}", fn () => $this->requestBuilder->runQuery('PersonDetails', [
|
||||
'slug' => $slug
|
||||
]));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
query ($id: ID!) {
|
||||
findPersonById(id: $id) {
|
||||
query ($slug: String!) {
|
||||
findPersonBySlug(slug: $slug) {
|
||||
id
|
||||
description
|
||||
birthday
|
||||
|
@ -22,6 +22,36 @@ query ($id: ID!) {
|
|||
canonical
|
||||
localized
|
||||
}
|
||||
mediaStaff {
|
||||
nodes {
|
||||
id
|
||||
role
|
||||
media {
|
||||
id
|
||||
slug
|
||||
type
|
||||
posterImage {
|
||||
original {
|
||||
height
|
||||
name
|
||||
url
|
||||
width
|
||||
}
|
||||
views {
|
||||
height
|
||||
name
|
||||
url
|
||||
width
|
||||
}
|
||||
}
|
||||
titles {
|
||||
alternatives
|
||||
canonical
|
||||
localized
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
voices {
|
||||
nodes {
|
||||
locale
|
||||
|
@ -29,6 +59,7 @@ query ($id: ID!) {
|
|||
role
|
||||
character {
|
||||
id
|
||||
slug
|
||||
image {
|
||||
original {
|
||||
height
|
||||
|
@ -43,6 +74,7 @@ query ($id: ID!) {
|
|||
}
|
||||
media {
|
||||
id
|
||||
slug
|
||||
posterImage {
|
||||
original {
|
||||
height
|
||||
|
|
|
@ -50,7 +50,7 @@ final class CharacterTransformer extends AbstractTransformer {
|
|||
|
||||
if (isset($data['media']['nodes']))
|
||||
{
|
||||
[$media, $castings] = $this->organizeMediaAndVoices($data['media']['nodes']);
|
||||
[$media, $castings] = $this->organizeMediaAndVoices($data['media']['nodes'] ?? []);
|
||||
}
|
||||
|
||||
return Character::from([
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
namespace Aviat\AnimeClient\API\Kitsu\Transformer;
|
||||
|
||||
use Aviat\AnimeClient\API\JsonAPI;
|
||||
use Aviat\AnimeClient\Kitsu;
|
||||
use Aviat\AnimeClient\Types\Person;
|
||||
use Aviat\Ion\Transformer\AbstractTransformer;
|
||||
|
||||
|
@ -31,14 +31,17 @@ final class PersonTransformer extends AbstractTransformer {
|
|||
*/
|
||||
public function transform($personData): Person
|
||||
{
|
||||
$data = JsonAPI::organizeData($personData);
|
||||
$included = JsonAPI::organizeIncludes($personData['included']);
|
||||
$data = $personData['data']['findPersonBySlug'] ?? [];
|
||||
$canonicalName = $data['names']['localized'][$data['names']['canonical']]
|
||||
?? array_shift($data['names']['localized']);
|
||||
|
||||
$orgData = $this->organizeData($included);
|
||||
$orgData = $this->organizeData($data);
|
||||
|
||||
return Person::from([
|
||||
'id' => $data['id'],
|
||||
'name' => $data['attributes']['name'],
|
||||
'name' => $canonicalName,
|
||||
'names' => array_diff($data['names']['localized'], [$canonicalName]),
|
||||
'description' => $data['description']['en'] ?? '',
|
||||
'characters' => $orgData['characters'],
|
||||
'staff' => $orgData['staff'],
|
||||
]);
|
||||
|
@ -47,88 +50,98 @@ final class PersonTransformer extends AbstractTransformer {
|
|||
protected function organizeData(array $data): array
|
||||
{
|
||||
$output = [
|
||||
'characters' => [
|
||||
'main' => [],
|
||||
'supporting' => [],
|
||||
],
|
||||
'characters' => [],
|
||||
'staff' => [],
|
||||
];
|
||||
|
||||
if (array_key_exists('characterVoices', $data))
|
||||
{
|
||||
foreach ($data['characterVoices'] as $cv)
|
||||
{
|
||||
$mcId = $cv['relationships']['mediaCharacter']['data']['id'];
|
||||
$characters = [];
|
||||
$staff = [];
|
||||
|
||||
if ( ! array_key_exists($mcId, $data['mediaCharacters']))
|
||||
if (count($data['mediaStaff']['nodes']) > 0)
|
||||
{
|
||||
$roles = array_unique(array_column($data['mediaStaff']['nodes'], 'role'));
|
||||
foreach ($roles as $role)
|
||||
{
|
||||
$staff[$role] = [];
|
||||
}
|
||||
ksort($staff);
|
||||
|
||||
foreach ($data['mediaStaff']['nodes'] as $staffing)
|
||||
{
|
||||
$media = $staffing['media'];
|
||||
$role = $staffing['role'];
|
||||
$title = $media['titles']['canonical'];
|
||||
$type = strtolower($media['type']);
|
||||
|
||||
$staff[$role][$type][$media['id']] = [
|
||||
'id' => $media['id'],
|
||||
'title' => $title,
|
||||
'titles' => array_merge([$title], Kitsu::getFilteredTitles($media['titles'])),
|
||||
'image' => [
|
||||
'original' => $media['posterImage']['views'][1]['url'],
|
||||
],
|
||||
'slug' => $media['slug'],
|
||||
];
|
||||
|
||||
uasort($staff[$role][$type], fn ($a, $b) => $a['title'] <=> $b['title']);
|
||||
}
|
||||
|
||||
$output['staff'] = $staff;
|
||||
}
|
||||
|
||||
if (count($data['voices']['nodes']) > 0)
|
||||
{
|
||||
foreach ($data['voices']['nodes'] as $voicing)
|
||||
{
|
||||
$character = $voicing['mediaCharacter']['character'];
|
||||
$charId = $character['id'];
|
||||
$rawMedia = $voicing['mediaCharacter']['media'];
|
||||
$role = strtolower($voicing['mediaCharacter']['role']);
|
||||
|
||||
$media = [
|
||||
'id' => $rawMedia['id'],
|
||||
'slug' => $rawMedia['slug'],
|
||||
'titles' => array_merge(
|
||||
[$rawMedia['titles']['canonical']],
|
||||
Kitsu::getFilteredTitles($rawMedia['titles']),
|
||||
),
|
||||
];
|
||||
|
||||
if ( ! isset($characters[$role][$charId]))
|
||||
{
|
||||
continue;
|
||||
if ( ! array_key_exists($role, $characters))
|
||||
{
|
||||
$characters[$role] = [];
|
||||
}
|
||||
|
||||
$characters[$role][$charId] = [
|
||||
'character' => [
|
||||
'id' => $character['id'],
|
||||
'slug' => $character['slug'],
|
||||
'image' => [
|
||||
'original' => $character['image']['original']['url'],
|
||||
],
|
||||
'canonicalName' => $character['names']['canonical'],
|
||||
],
|
||||
'media' => [
|
||||
$media['id'] => $media
|
||||
],
|
||||
];
|
||||
}
|
||||
else
|
||||
{
|
||||
$characters[$role][$charId]['media'][$media['id']] = $media;
|
||||
}
|
||||
|
||||
$mc = $data['mediaCharacters'][$mcId];
|
||||
|
||||
$role = $mc['role'];
|
||||
|
||||
$charId = $mc['relationships']['character']['data']['id'];
|
||||
$mediaId = $mc['relationships']['media']['data']['id'];
|
||||
|
||||
$existingMedia = array_key_exists($charId, $output['characters'][$role])
|
||||
? $output['characters'][$role][$charId]['media']
|
||||
: [];
|
||||
|
||||
$relatedMedia = [
|
||||
$mediaId => $data['anime'][$mediaId],
|
||||
];
|
||||
|
||||
$includedMedia = array_replace_recursive($existingMedia, $relatedMedia);
|
||||
|
||||
uasort($includedMedia, static function ($a, $b) {
|
||||
return $a['canonicalTitle'] <=> $b['canonicalTitle'];
|
||||
});
|
||||
|
||||
$character = $data['characters'][$charId];
|
||||
|
||||
$output['characters'][$role][$charId] = [
|
||||
'character' => $character,
|
||||
'media' => $includedMedia,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists('mediaStaff', $data))
|
||||
{
|
||||
foreach ($data['mediaStaff'] as $rid => $role)
|
||||
{
|
||||
$roleName = $role['role'];
|
||||
$mediaType = $role['relationships']['media']['data']['type'];
|
||||
$mediaId = $role['relationships']['media']['data']['id'];
|
||||
$media = $data[$mediaType][$mediaId];
|
||||
$output['staff'][$roleName][$mediaType][$mediaId] = $media;
|
||||
}
|
||||
}
|
||||
|
||||
uasort($output['characters']['main'], static function ($a, $b) {
|
||||
return $a['character']['canonicalName'] <=> $b['character']['canonicalName'];
|
||||
});
|
||||
uasort($output['characters']['supporting'], static function ($a, $b) {
|
||||
return $a['character']['canonicalName'] <=> $b['character']['canonicalName'];
|
||||
});
|
||||
ksort($output['staff']);
|
||||
foreach ($output['staff'] as $role => &$media)
|
||||
{
|
||||
if (array_key_exists('anime', $media))
|
||||
{
|
||||
uasort($media['anime'], static function ($a, $b) {
|
||||
return $a['canonicalTitle'] <=> $b['canonicalTitle'];
|
||||
});
|
||||
uasort(
|
||||
$characters[$role][$charId]['media'],
|
||||
fn ($a, $b) => $a['titles'][0] <=> $b['titles'][0]
|
||||
);
|
||||
}
|
||||
|
||||
if (array_key_exists('manga', $media))
|
||||
{
|
||||
uasort($media['manga'], static function ($a, $b) {
|
||||
return $a['canonicalTitle'] <=> $b['canonicalTitle'];
|
||||
});
|
||||
}
|
||||
krsort($characters);
|
||||
|
||||
$output['characters'] = $characters;
|
||||
}
|
||||
|
||||
return $output;
|
||||
|
|
|
@ -402,10 +402,9 @@ type Anime implements Episodic & Media & WithTimestamps {
|
|||
youtubeTrailerVideoId: String
|
||||
}
|
||||
|
||||
type AnimeAmountConsumed implements AmountConsumed & WithTimestamps {
|
||||
type AnimeAmountConsumed implements AmountConsumed {
|
||||
"Total media completed atleast once."
|
||||
completed: Int!
|
||||
createdAt: ISO8601DateTime!
|
||||
id: ID!
|
||||
"Total amount of media."
|
||||
media: Int!
|
||||
|
@ -417,13 +416,11 @@ type AnimeAmountConsumed implements AmountConsumed & WithTimestamps {
|
|||
time: Int!
|
||||
"Total progress of library including reconsuming."
|
||||
units: Int!
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
type AnimeCategoryBreakdown implements CategoryBreakdown & WithTimestamps {
|
||||
type AnimeCategoryBreakdown implements CategoryBreakdown {
|
||||
"A Map of category_id -> count for all categories present on the library entries"
|
||||
categories: Map!
|
||||
createdAt: ISO8601DateTime!
|
||||
id: ID!
|
||||
"The profile related to the user for this stat."
|
||||
profile: Profile!
|
||||
|
@ -431,7 +428,6 @@ type AnimeCategoryBreakdown implements CategoryBreakdown & WithTimestamps {
|
|||
recalculatedAt: ISO8601Date!
|
||||
"The total amount of library entries."
|
||||
total: Int!
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
"The connection type for Anime."
|
||||
|
@ -468,13 +464,12 @@ type AnimeEdge {
|
|||
node: Anime
|
||||
}
|
||||
|
||||
type AnimeMutation implements WithTimestamps {
|
||||
type AnimeMutation {
|
||||
"Create an Anime."
|
||||
create(
|
||||
"Create an Anime."
|
||||
input: AnimeCreateInput!
|
||||
): AnimeCreatePayload
|
||||
createdAt: ISO8601DateTime!
|
||||
"Delete an Anime."
|
||||
delete(
|
||||
"Delete an Anime."
|
||||
|
@ -485,7 +480,6 @@ type AnimeMutation implements WithTimestamps {
|
|||
"Update an Anime."
|
||||
input: AnimeUpdateInput!
|
||||
): AnimeUpdatePayload
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
"Autogenerated return type of AnimeUpdate"
|
||||
|
@ -739,6 +733,20 @@ type EpisodeConnection {
|
|||
totalCount: Int!
|
||||
}
|
||||
|
||||
"Autogenerated return type of EpisodeCreate"
|
||||
type EpisodeCreatePayload {
|
||||
episode: Episode
|
||||
"Graphql Errors"
|
||||
errors: [Generic!]
|
||||
}
|
||||
|
||||
"Autogenerated return type of EpisodeDelete"
|
||||
type EpisodeDeletePayload {
|
||||
episode: GenericDelete
|
||||
"Graphql Errors"
|
||||
errors: [Generic!]
|
||||
}
|
||||
|
||||
"An edge in a connection."
|
||||
type EpisodeEdge {
|
||||
"A cursor for use in pagination."
|
||||
|
@ -747,6 +755,31 @@ type EpisodeEdge {
|
|||
node: Episode
|
||||
}
|
||||
|
||||
type EpisodeMutation {
|
||||
"Create an Episode."
|
||||
create(
|
||||
"Create an Episode"
|
||||
input: EpisodeCreateInput!
|
||||
): EpisodeCreatePayload
|
||||
"Delete an Episode."
|
||||
delete(
|
||||
"Delete an Episode"
|
||||
input: GenericDeleteInput!
|
||||
): EpisodeDeletePayload
|
||||
"Update an Episode."
|
||||
update(
|
||||
"Update an Episode"
|
||||
input: EpisodeUpdateInput!
|
||||
): EpisodeUpdatePayload
|
||||
}
|
||||
|
||||
"Autogenerated return type of EpisodeUpdate"
|
||||
type EpisodeUpdatePayload {
|
||||
episode: Episode
|
||||
"Graphql Errors"
|
||||
errors: [Generic!]
|
||||
}
|
||||
|
||||
"Favorite media, characters, and people for a user"
|
||||
type Favorite implements WithTimestamps {
|
||||
createdAt: ISO8601DateTime!
|
||||
|
@ -778,30 +811,24 @@ type FavoriteEdge {
|
|||
node: Favorite
|
||||
}
|
||||
|
||||
type Generic implements Base & WithTimestamps {
|
||||
type Generic implements Base {
|
||||
"The error code."
|
||||
code: String
|
||||
createdAt: ISO8601DateTime!
|
||||
"A description of the error"
|
||||
message: String!
|
||||
"Which input value this error came from"
|
||||
path: [String!]
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
type GenericDelete implements WithTimestamps {
|
||||
createdAt: ISO8601DateTime!
|
||||
type GenericDelete {
|
||||
id: ID!
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
type Image implements WithTimestamps {
|
||||
type Image {
|
||||
"A blurhash-encoded version of this image"
|
||||
blurhash: String
|
||||
createdAt: ISO8601DateTime!
|
||||
"The original image"
|
||||
original: ImageView!
|
||||
updatedAt: ISO8601DateTime!
|
||||
"The various generated views of this image"
|
||||
views(names: [String!]): [ImageView!]!
|
||||
}
|
||||
|
@ -820,7 +847,7 @@ type ImageView implements WithTimestamps {
|
|||
}
|
||||
|
||||
"The user library filterable by media_type and status"
|
||||
type Library implements WithTimestamps {
|
||||
type Library {
|
||||
"All Library Entries for a specific Media"
|
||||
all(
|
||||
"Returns the elements in the list that come after the specified cursor."
|
||||
|
@ -846,7 +873,6 @@ type Library implements WithTimestamps {
|
|||
last: Int,
|
||||
mediaType: media_type!
|
||||
): LibraryEntryConnection!
|
||||
createdAt: ISO8601DateTime!
|
||||
"Library Entries for a specific Media filtered by the current status"
|
||||
current(
|
||||
"Returns the elements in the list that come after the specified cursor."
|
||||
|
@ -895,7 +921,6 @@ type Library implements WithTimestamps {
|
|||
last: Int,
|
||||
mediaType: media_type!
|
||||
): LibraryEntryConnection!
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
"Information about a specific media entry for a user"
|
||||
|
@ -984,13 +1009,12 @@ type LibraryEntryEdge {
|
|||
node: LibraryEntry
|
||||
}
|
||||
|
||||
type LibraryEntryMutation implements WithTimestamps {
|
||||
type LibraryEntryMutation {
|
||||
"Create a library entry"
|
||||
create(
|
||||
"Create a Library Entry"
|
||||
input: LibraryEntryCreateInput!
|
||||
): LibraryEntryCreatePayload
|
||||
createdAt: ISO8601DateTime!
|
||||
"Delete a library entry"
|
||||
delete(
|
||||
"Delete Library Entry"
|
||||
|
@ -1001,17 +1025,36 @@ type LibraryEntryMutation implements WithTimestamps {
|
|||
"Update Library Entry"
|
||||
input: LibraryEntryUpdateInput!
|
||||
): LibraryEntryUpdatePayload
|
||||
"Update a library entry status by id"
|
||||
"Update library entry progress by id"
|
||||
updateProgressById(
|
||||
"Update library entry progress by id"
|
||||
input: UpdateProgressByIdInput!
|
||||
): LibraryEntryUpdateProgressByIdPayload
|
||||
"Update library entry progress by media"
|
||||
updateProgressByMedia(
|
||||
"Update library entry progress by media"
|
||||
input: UpdateProgressByMediaInput!
|
||||
): LibraryEntryUpdateProgressByMediaPayload
|
||||
"Update library entry rating by id"
|
||||
updateRatingById(
|
||||
"Update library entry rating by id"
|
||||
input: UpdateRatingByIdInput!
|
||||
): LibraryEntryUpdateRatingByIdPayload
|
||||
"Update library entry rating by media"
|
||||
updateRatingByMedia(
|
||||
"Update library entry rating by media"
|
||||
input: UpdateRatingByMediaInput!
|
||||
): LibraryEntryUpdateRatingByMediaPayload
|
||||
"Update library entry status by id"
|
||||
updateStatusById(
|
||||
"Update a library entry status by id"
|
||||
"Update library entry status by id"
|
||||
input: UpdateStatusByIdInput!
|
||||
): LibraryEntryUpdateStatusByIdPayload
|
||||
"Update a library entry status by media"
|
||||
"Update library entry status by media"
|
||||
updateStatusByMedia(
|
||||
"Update a library entry status by media"
|
||||
"Update library entry status by media"
|
||||
input: UpdateStatusByMediaInput!
|
||||
): LibraryEntryUpdateStatusByMediaPayload
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
"Autogenerated return type of LibraryEntryUpdate"
|
||||
|
@ -1021,6 +1064,34 @@ type LibraryEntryUpdatePayload {
|
|||
libraryEntry: LibraryEntry
|
||||
}
|
||||
|
||||
"Autogenerated return type of LibraryEntryUpdateProgressById"
|
||||
type LibraryEntryUpdateProgressByIdPayload {
|
||||
"Graphql Errors"
|
||||
errors: [Generic!]
|
||||
libraryEntry: LibraryEntry
|
||||
}
|
||||
|
||||
"Autogenerated return type of LibraryEntryUpdateProgressByMedia"
|
||||
type LibraryEntryUpdateProgressByMediaPayload {
|
||||
"Graphql Errors"
|
||||
errors: [Generic!]
|
||||
libraryEntry: LibraryEntry
|
||||
}
|
||||
|
||||
"Autogenerated return type of LibraryEntryUpdateRatingById"
|
||||
type LibraryEntryUpdateRatingByIdPayload {
|
||||
"Graphql Errors"
|
||||
errors: [Generic!]
|
||||
libraryEntry: LibraryEntry
|
||||
}
|
||||
|
||||
"Autogenerated return type of LibraryEntryUpdateRatingByMedia"
|
||||
type LibraryEntryUpdateRatingByMediaPayload {
|
||||
"Graphql Errors"
|
||||
errors: [Generic!]
|
||||
libraryEntry: LibraryEntry
|
||||
}
|
||||
|
||||
"Autogenerated return type of LibraryEntryUpdateStatusById"
|
||||
type LibraryEntryUpdateStatusByIdPayload {
|
||||
"Graphql Errors"
|
||||
|
@ -1210,10 +1281,9 @@ type Manga implements Media & WithTimestamps {
|
|||
volumeCount: Int
|
||||
}
|
||||
|
||||
type MangaAmountConsumed implements AmountConsumed & WithTimestamps {
|
||||
type MangaAmountConsumed implements AmountConsumed {
|
||||
"Total media completed atleast once."
|
||||
completed: Int!
|
||||
createdAt: ISO8601DateTime!
|
||||
id: ID!
|
||||
"Total amount of media."
|
||||
media: Int!
|
||||
|
@ -1223,13 +1293,11 @@ type MangaAmountConsumed implements AmountConsumed & WithTimestamps {
|
|||
recalculatedAt: ISO8601Date!
|
||||
"Total progress of library including reconsuming."
|
||||
units: Int!
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
type MangaCategoryBreakdown implements CategoryBreakdown & WithTimestamps {
|
||||
type MangaCategoryBreakdown implements CategoryBreakdown {
|
||||
"A Map of category_id -> count for all categories present on the library entries"
|
||||
categories: Map!
|
||||
createdAt: ISO8601DateTime!
|
||||
id: ID!
|
||||
"The profile related to the user for this stat."
|
||||
profile: Profile!
|
||||
|
@ -1237,7 +1305,6 @@ type MangaCategoryBreakdown implements CategoryBreakdown & WithTimestamps {
|
|||
recalculatedAt: ISO8601Date!
|
||||
"The total amount of library entries."
|
||||
total: Int!
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
"The connection type for Manga."
|
||||
|
@ -1470,12 +1537,11 @@ type MediaStaffEdge {
|
|||
node: MediaStaff
|
||||
}
|
||||
|
||||
type Mutation implements WithTimestamps {
|
||||
type Mutation {
|
||||
anime: AnimeMutation
|
||||
createdAt: ISO8601DateTime!
|
||||
episode: EpisodeMutation
|
||||
libraryEntry: LibraryEntryMutation
|
||||
pro: ProMutation!
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
"Information about pagination in a connection."
|
||||
|
@ -1490,11 +1556,7 @@ type PageInfo {
|
|||
startCursor: String
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
A Voice Actor, Director, Animator, or other person who works in the creation and\
|
||||
localization of media
|
||||
"""
|
||||
"A Voice Actor, Director, Animator, or other person who works in the creation and localization of media"
|
||||
type Person implements WithTimestamps {
|
||||
"The day when this person was born"
|
||||
birthday: Date
|
||||
|
@ -1504,6 +1566,17 @@ type Person implements WithTimestamps {
|
|||
id: ID!
|
||||
"An image of the person"
|
||||
image: Image
|
||||
"Information about the person working on specific media"
|
||||
mediaStaff(
|
||||
"Returns the elements in the list that come after the specified cursor."
|
||||
after: String,
|
||||
"Returns the elements in the list that come before the specified cursor."
|
||||
before: String,
|
||||
"Returns the first _n_ elements from the list."
|
||||
first: Int,
|
||||
"Returns the last _n_ elements from the list."
|
||||
last: Int
|
||||
): MediaStaffConnection
|
||||
"The primary name of this person."
|
||||
name: String!
|
||||
"The name of this person in various languages"
|
||||
|
@ -1596,8 +1669,7 @@ type PostEdge {
|
|||
node: Post
|
||||
}
|
||||
|
||||
type ProMutation implements WithTimestamps {
|
||||
createdAt: ISO8601DateTime!
|
||||
type ProMutation {
|
||||
"Set the user's discord tag"
|
||||
setDiscord(
|
||||
"Your discord tag (Name#1234)"
|
||||
|
@ -1610,7 +1682,6 @@ type ProMutation implements WithTimestamps {
|
|||
): SetMessagePayload
|
||||
"End the user's pro subscription"
|
||||
unsubscribe: UnsubscribePayload
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
"A subscription to Kitsu PRO"
|
||||
|
@ -1719,11 +1790,7 @@ type Profile implements WithTimestamps {
|
|||
"Returns the last _n_ elements from the list."
|
||||
last: Int
|
||||
): MediaReactionConnection!
|
||||
"""
|
||||
|
||||
A non-unique publicly visible name for the profile.
|
||||
Minimum of 3 characters and any valid Unicode character
|
||||
"""
|
||||
"A non-unique publicly visible name for the profile. Minimum of 3 characters and any valid Unicode character"
|
||||
name: String!
|
||||
"Post pinned to the user profile"
|
||||
pinnedPost: Post
|
||||
|
@ -1787,17 +1854,15 @@ type ProfileEdge {
|
|||
}
|
||||
|
||||
"The different types of user stats that we calculate."
|
||||
type ProfileStats implements WithTimestamps {
|
||||
type ProfileStats {
|
||||
"The total amount of anime you have watched over your whole life."
|
||||
animeAmountConsumed: AnimeAmountConsumed!
|
||||
"The breakdown of the different categories related to the anime you have completed"
|
||||
animeCategoryBreakdown: AnimeCategoryBreakdown!
|
||||
createdAt: ISO8601DateTime!
|
||||
"The total amount of manga you ahve read over your whole life."
|
||||
mangaAmountConsumed: MangaAmountConsumed!
|
||||
"The breakdown of the different categories related to the manga you have completed"
|
||||
mangaCategoryBreakdown: MangaCategoryBreakdown!
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
type Query {
|
||||
|
@ -2017,13 +2082,11 @@ type QuoteLineEdge {
|
|||
}
|
||||
|
||||
"Information about a user session"
|
||||
type Session implements WithTimestamps {
|
||||
type Session {
|
||||
"The account associated with this session"
|
||||
account: Account
|
||||
createdAt: ISO8601DateTime!
|
||||
"The profile associated with this session"
|
||||
profile: Profile
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
"Autogenerated return type of SetDiscord"
|
||||
|
@ -2141,17 +2204,15 @@ type StreamingLinkEdge {
|
|||
node: StreamingLink
|
||||
}
|
||||
|
||||
type TitlesList implements WithTimestamps {
|
||||
type TitlesList {
|
||||
"A list of additional, alternative, abbreviated, or unofficial titles"
|
||||
alternatives: [String!]
|
||||
"The official or de facto international title"
|
||||
canonical: String
|
||||
"The locale code that identifies which title is used as the canonical title"
|
||||
canonicalLocale: String
|
||||
createdAt: ISO8601DateTime!
|
||||
"The list of localized titles keyed by locale"
|
||||
localized(locales: [String!]): Map!
|
||||
updatedAt: ISO8601DateTime!
|
||||
}
|
||||
|
||||
"Autogenerated return type of Unsubscribe"
|
||||
|
@ -2424,6 +2485,27 @@ input AnimeUpdateInput {
|
|||
youtubeTrailerVideoId: String
|
||||
}
|
||||
|
||||
input EpisodeCreateInput {
|
||||
description: Map
|
||||
length: Int
|
||||
mediaId: ID!
|
||||
mediaType: media_type!
|
||||
number: Int!
|
||||
releasedAt: Date
|
||||
thumbnailImage: Upload
|
||||
titles: TitlesListInput!
|
||||
}
|
||||
|
||||
input EpisodeUpdateInput {
|
||||
description: Map
|
||||
id: ID!
|
||||
length: Int
|
||||
number: Int
|
||||
releasedAt: Date
|
||||
thumbnailImage: Upload
|
||||
titles: TitlesListInput
|
||||
}
|
||||
|
||||
input GenericDeleteInput {
|
||||
id: ID!
|
||||
}
|
||||
|
@ -2464,6 +2546,30 @@ input TitlesListInput {
|
|||
localized: Map
|
||||
}
|
||||
|
||||
input UpdateProgressByIdInput {
|
||||
id: ID!
|
||||
progress: Int!
|
||||
}
|
||||
|
||||
input UpdateProgressByMediaInput {
|
||||
mediaId: ID!
|
||||
mediaType: media_type!
|
||||
progress: Int!
|
||||
}
|
||||
|
||||
input UpdateRatingByIdInput {
|
||||
id: ID!
|
||||
"A number between 2 - 20"
|
||||
rating: Int!
|
||||
}
|
||||
|
||||
input UpdateRatingByMediaInput {
|
||||
mediaId: ID!
|
||||
mediaType: media_type!
|
||||
"A number between 2 - 20"
|
||||
rating: Int!
|
||||
}
|
||||
|
||||
input UpdateStatusByIdInput {
|
||||
id: ID!
|
||||
status: LibraryEntryStatus!
|
||||
|
|
|
@ -38,6 +38,17 @@ final class Tabs {
|
|||
bool $hasSectionWrapper = false
|
||||
): string
|
||||
{
|
||||
if (count($tabData) < 2)
|
||||
{
|
||||
return $this->render('single-tab.php', [
|
||||
'name' => $name,
|
||||
'data' => $tabData,
|
||||
'callback' => $cb,
|
||||
'className' => $className . ' single-tab',
|
||||
'hasSectionWrapper' => $hasSectionWrapper,
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->render('tabs.php', [
|
||||
'name' => $name,
|
||||
'data' => $tabData,
|
||||
|
|
|
@ -50,15 +50,14 @@ final class People extends BaseController {
|
|||
/**
|
||||
* Show information about a person
|
||||
*
|
||||
* @param string $id
|
||||
* @param string|null $slug
|
||||
* @param string $slug
|
||||
* @return void
|
||||
* @throws ContainerException
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function index(string $id, ?string $slug = NULL): void
|
||||
public function index(string $slug): void
|
||||
{
|
||||
$rawData = $this->model->getPerson($id, $slug);
|
||||
$rawData = $this->model->getPerson($slug);
|
||||
$data = (new PersonTransformer())->transform($rawData)->toArray();
|
||||
|
||||
if (( ! array_key_exists('data', $rawData)) || empty($rawData['data']))
|
||||
|
|
|
@ -20,28 +20,16 @@ namespace Aviat\AnimeClient\Types;
|
|||
* Type representing a person for display
|
||||
*/
|
||||
final class Person extends AbstractType {
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public ?string $name;
|
||||
|
||||
/**
|
||||
* @var Characters
|
||||
*/
|
||||
public ?Characters $characters;
|
||||
public array $names = [];
|
||||
|
||||
public ?string $description;
|
||||
|
||||
public array $characters = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public array $staff = [];
|
||||
|
||||
public function setCharacters($characters): void
|
||||
{
|
||||
$this->characters = Characters::from($characters);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue